From c1cb5953820f26d4d0a614650bc8c50cbc5a3ce6 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 15 Dec 2020 15:37:37 +0100 Subject: Move to upstream `macro_rules!` model --- Cargo.lock | 4 +- Cargo.toml | 2 + crates/completion/src/render/macro_.rs | 6 +- crates/hir/src/has_source.rs | 4 +- crates/hir/src/semantics.rs | 2 +- crates/hir/src/semantics/source_to_def.rs | 5 +- crates/hir_def/src/body/lower.rs | 134 +++++++++++---------- crates/hir_def/src/item_tree.rs | 19 ++- crates/hir_def/src/item_tree/lower.rs | 24 ++-- crates/hir_def/src/nameres/collector.rs | 65 +++++----- crates/hir_def/src/path/lower.rs | 8 +- crates/hir_expand/src/builtin_macro.rs | 16 ++- crates/hir_expand/src/lib.rs | 2 +- crates/hir_ty/src/tests/macros.rs | 1 - crates/ide/src/file_structure.rs | 14 +-- crates/ide/src/references.rs | 2 +- crates/ide/src/syntax_highlighting.rs | 52 ++++---- .../test_data/highlight_doctest.html | 2 +- .../test_data/highlight_strings.html | 6 +- .../test_data/highlighting.html | 6 +- crates/ide_db/src/defs.rs | 2 +- crates/ide_db/src/symbol_index.rs | 8 +- crates/mbe/src/mbe_expander.rs | 2 +- crates/mbe/src/tests.rs | 4 +- crates/parser/src/grammar/items.rs | 44 +++++-- crates/parser/src/syntax_kind/generated.rs | 7 +- crates/syntax/src/ast/generated/nodes.rs | 43 ++++++- crates/syntax/src/ast/node_ext.rs | 17 +-- crates/syntax/src/display.rs | 2 +- crates/syntax/src/parsing/text_tree_sink.rs | 4 +- .../parser/err/0002_duplicate_shebang.rast | 14 ++- .../parser/inline/ok/0062_mod_contents.rast | 7 +- .../parser/inline/ok/0096_no_semi_after_block.rast | 48 ++++---- .../parser/inline/ok/0160_try_macro_rules.rast | 7 +- .../ok/0053_outer_attribute_on_macro_rules.rast | 7 +- xtask/src/ast_src.rs | 4 +- 36 files changed, 322 insertions(+), 272 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec8ad11cb..ff2c33f45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1827,9 +1827,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] name = "ungrammar" -version = "1.1.4" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0cd89993af555540e2436fc6adb8479b0dbe386339a136397952e9c89e17a9" +checksum = "873186a460627379e7e28880a0d33b729c205634f6f021321f50b323235e62d7" [[package]] name = "unicase" diff --git a/Cargo.toml b/Cargo.toml index e30ef0e2f..59d36fbc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,3 +25,5 @@ debug = 0 # Set this to 1 or 2 to get more useful backtraces in debugger. # chalk-solve = { path = "../chalk/chalk-solve" } # chalk-ir = { path = "../chalk/chalk-ir" } # chalk-recursive = { path = "../chalk/chalk-recursive" } + +# ungrammar = { path = "../ungrammar" } diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs index eb3209bee..6cfbd6c9b 100644 --- a/crates/completion/src/render/macro_.rs +++ b/crates/completion/src/render/macro_.rs @@ -144,7 +144,7 @@ mod tests { use foo::<|>; //- /foo/lib.rs crate:foo #[macro_export] -macro_rules frobnicate { () => () } +macro_rules! frobnicate { () => () } "#, r#" use foo::frobnicate; @@ -154,11 +154,11 @@ use foo::frobnicate; check_edit( "frobnicate!", r#" -macro_rules frobnicate { () => () } +macro_rules! frobnicate { () => () } fn main() { frob<|>!(); } "#, r#" -macro_rules frobnicate { () => () } +macro_rules! frobnicate { () => () } fn main() { frobnicate!(); } "#, ); diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs index 1e64a1614..107ad0690 100644 --- a/crates/hir/src/has_source.rs +++ b/crates/hir/src/has_source.rs @@ -110,8 +110,8 @@ impl HasSource for TypeAlias { } } impl HasSource for MacroDef { - type Ast = ast::MacroCall; - fn source(self, db: &dyn HirDatabase) -> InFile { + type Ast = ast::MacroRules; + fn source(self, db: &dyn HirDatabase) -> InFile { InFile { file_id: self.id.ast_id.expect("MacroDef without ast_id").file_id, value: self.id.ast_id.expect("MacroDef without ast_id").to_node(db.upcast()), diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index e4fc21ced..5959ac4ca 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -723,7 +723,7 @@ to_def_impls![ (crate::EnumVariant, ast::Variant, enum_variant_to_def), (crate::TypeParam, ast::TypeParam, type_param_to_def), (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def), - (crate::MacroDef, ast::MacroCall, macro_call_to_def), // this one is dubious, not all calls are macros + (crate::MacroDef, ast::MacroRules, macro_rules_to_def), (crate::Local, ast::IdentPat, bind_pat_to_def), ]; diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index badcf0ae8..a333d7aea 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -149,7 +149,10 @@ impl SourceToDefCtx<'_, '_> { } // FIXME: use DynMap as well? - pub(super) fn macro_call_to_def(&mut self, src: InFile) -> Option { + pub(super) fn macro_rules_to_def( + &mut self, + src: InFile, + ) -> Option { let kind = MacroDefKind::Declarative; let file_id = src.file_id.original_file(self.db.upcast()); let krate = self.file_to_def(file_id)?.krate; diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 6c0de3ee8..bdba4c33e 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -566,66 +566,52 @@ impl ExprCollector<'_> { syntax_ptr: AstPtr, mut collector: F, ) { - if let Some(name) = e.is_macro_rules().map(|it| it.as_name()) { - let mac = MacroDefId { - krate: Some(self.expander.module.krate), - ast_id: Some(self.expander.ast_id(&e)), - kind: MacroDefKind::Declarative, - local_inner: false, - }; - self.body.item_scope.define_legacy_macro(name, mac); + // File containing the macro call. Expansion errors will be attached here. + let outer_file = self.expander.current_file_id; - // FIXME: do we still need to allocate this as missing ? - collector(self, None); - } else { - // File containing the macro call. Expansion errors will be attached here. - let outer_file = self.expander.current_file_id; - - let macro_call = self.expander.to_source(AstPtr::new(&e)); - let res = self.expander.enter_expand(self.db, Some(&self.body.item_scope), e); - - match &res.err { - Some(ExpandError::UnresolvedProcMacro) => { - self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro( - UnresolvedProcMacro { - file: outer_file, - node: syntax_ptr.into(), - precise_location: None, - macro_name: None, - }, - )); - } - Some(err) => { - self.source_map.diagnostics.push(BodyDiagnostic::MacroError(MacroError { + let macro_call = self.expander.to_source(AstPtr::new(&e)); + let res = self.expander.enter_expand(self.db, Some(&self.body.item_scope), e); + + match &res.err { + Some(ExpandError::UnresolvedProcMacro) => { + self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro( + UnresolvedProcMacro { file: outer_file, node: syntax_ptr.into(), - message: err.to_string(), - })); - } - None => {} + precise_location: None, + macro_name: None, + }, + )); + } + Some(err) => { + self.source_map.diagnostics.push(BodyDiagnostic::MacroError(MacroError { + file: outer_file, + node: syntax_ptr.into(), + message: err.to_string(), + })); } + None => {} + } - match res.value { - Some((mark, expansion)) => { - // FIXME: Statements are too complicated to recover from error for now. - // It is because we don't have any hygenine for local variable expansion right now. - if T::can_cast(syntax::SyntaxKind::MACRO_STMTS) && res.err.is_some() { - self.expander.exit(self.db, mark); - collector(self, None); - } else { - self.source_map - .expansions - .insert(macro_call, self.expander.current_file_id); + match res.value { + Some((mark, expansion)) => { + // FIXME: Statements are too complicated to recover from error for now. + // It is because we don't have any hygenine for local variable expansion right now. + if T::can_cast(syntax::SyntaxKind::MACRO_STMTS) && res.err.is_some() { + self.expander.exit(self.db, mark); + collector(self, None); + } else { + self.source_map.expansions.insert(macro_call, self.expander.current_file_id); - let item_tree = self.db.item_tree(self.expander.current_file_id); - self.item_trees.insert(self.expander.current_file_id, item_tree); + let item_tree = self.db.item_tree(self.expander.current_file_id); + self.item_trees.insert(self.expander.current_file_id, item_tree); - collector(self, Some(expansion)); - self.expander.exit(self.db, mark); - } + let id = collector(self, Some(expansion)); + self.expander.exit(self.db, mark); + id } - None => collector(self, None), } + None => collector(self, None), } } @@ -785,26 +771,44 @@ impl ExprCollector<'_> { | ast::Item::ExternCrate(_) | ast::Item::Module(_) | ast::Item::MacroCall(_) => return None, + ast::Item::MacroRules(def) => { + return Some(Either::Right(def)); + } }; - Some((def, name)) + Some(Either::Left((def, name))) }) .collect::>(); - for (def, name) in items { - self.body.item_scope.define_def(def); - if let Some(name) = name { - let vis = crate::visibility::Visibility::Public; // FIXME determine correctly - let has_constructor = match def { - ModuleDefId::AdtId(AdtId::StructId(s)) => { - self.db.struct_data(s).variant_data.kind() != StructKind::Record + for either in items { + match either { + Either::Left((def, name)) => { + self.body.item_scope.define_def(def); + if let Some(name) = name { + let vis = crate::visibility::Visibility::Public; // FIXME determine correctly + let has_constructor = match def { + ModuleDefId::AdtId(AdtId::StructId(s)) => { + self.db.struct_data(s).variant_data.kind() != StructKind::Record + } + _ => true, + }; + self.body.item_scope.push_res( + name.as_name(), + crate::per_ns::PerNs::from_def(def, vis, has_constructor), + ); } - _ => true, - }; - self.body.item_scope.push_res( - name.as_name(), - crate::per_ns::PerNs::from_def(def, vis, has_constructor), - ); + } + Either::Right(e) => { + let mac = MacroDefId { + krate: Some(self.expander.module.krate), + ast_id: Some(self.expander.ast_id(&e)), + kind: MacroDefKind::Declarative, + local_inner: false, + }; + if let Some(name) = e.name() { + self.body.item_scope.define_legacy_macro(name.as_name(), mac); + } + } } } } diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 864fad170..1c9babf37 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs @@ -142,6 +142,7 @@ impl ItemTree { type_aliases, mods, macro_calls, + macro_rules, exprs, vis, generics, @@ -162,6 +163,7 @@ impl ItemTree { type_aliases.shrink_to_fit(); mods.shrink_to_fit(); macro_calls.shrink_to_fit(); + macro_rules.shrink_to_fit(); exprs.shrink_to_fit(); vis.arena.shrink_to_fit(); @@ -280,6 +282,7 @@ struct ItemTreeData { type_aliases: Arena, mods: Arena, macro_calls: Arena, + macro_rules: Arena, exprs: Arena, vis: ItemVisibilities, @@ -427,6 +430,7 @@ mod_items! { TypeAlias in type_aliases -> ast::TypeAlias, Mod in mods -> ast::Module, MacroCall in macro_calls -> ast::MacroCall, + MacroRules in macro_rules -> ast::MacroRules, } macro_rules! impl_index { @@ -629,17 +633,22 @@ pub enum ModKind { #[derive(Debug, Clone, Eq, PartialEq)] pub struct MacroCall { - /// For `macro_rules!` declarations, this is the name of the declared macro. - pub name: Option, /// Path to the called macro. pub path: ModPath, + pub ast_id: FileAstId, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct MacroRules { + /// For `macro_rules!` declarations, this is the name of the declared macro. + pub name: Name, /// Has `#[macro_export]`. pub is_export: bool, /// Has `#[macro_export(local_inner_macros)]`. pub is_local_inner: bool, /// Has `#[rustc_builtin_macro]`. pub is_builtin: bool, - pub ast_id: FileAstId, + pub ast_id: FileAstId, } // NB: There's no `FileAstId` for `Expr`. The only case where this would be useful is for array @@ -670,7 +679,8 @@ impl ModItem { | ModItem::Static(_) | ModItem::Trait(_) | ModItem::Impl(_) - | ModItem::Mod(_) => None, + | ModItem::Mod(_) + | ModItem::MacroRules(_) => None, ModItem::MacroCall(call) => Some(AssocItem::MacroCall(*call)), ModItem::Const(konst) => Some(AssocItem::Const(*konst)), ModItem::TypeAlias(alias) => Some(AssocItem::TypeAlias(*alias)), @@ -697,6 +707,7 @@ impl ModItem { ModItem::TypeAlias(it) => tree[it.index].ast_id().upcast(), ModItem::Mod(it) => tree[it.index].ast_id().upcast(), ModItem::MacroCall(it) => tree[it.index].ast_id().upcast(), + ModItem::MacroRules(it) => tree[it.index].ast_id().upcast(), } } } diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index 2939c6b1e..b39d7fb7a 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs @@ -84,8 +84,7 @@ impl Ctx { | ast::Item::Fn(_) | ast::Item::TypeAlias(_) | ast::Item::Const(_) - | ast::Item::Static(_) - | ast::Item::MacroCall(_) => { + | ast::Item::Static(_) => { // Skip this if we're already collecting inner items. We'll descend into all nodes // already. if !inner { @@ -98,7 +97,11 @@ impl Ctx { ast::Item::Trait(_) | ast::Item::Impl(_) | ast::Item::ExternBlock(_) => {} // These don't have inner items. - ast::Item::Module(_) | ast::Item::ExternCrate(_) | ast::Item::Use(_) => {} + ast::Item::Module(_) + | ast::Item::ExternCrate(_) + | ast::Item::Use(_) + | ast::Item::MacroCall(_) + | ast::Item::MacroRules(_) => {} }; let attrs = Attrs::new(item, &self.hygiene); @@ -118,6 +121,7 @@ impl Ctx { )), ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast).map(Into::into), ast::Item::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into), + ast::Item::MacroRules(ast) => self.lower_macro_rules(ast).map(Into::into), ast::Item::ExternBlock(ast) => { Some(ModItems(self.lower_extern_block(ast).into_iter().collect::>())) } @@ -525,9 +529,15 @@ impl Ctx { } fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option> { - let name = m.name().map(|it| it.as_name()); - let attrs = Attrs::new(m, &self.hygiene); let path = ModPath::from_src(m.path()?, &self.hygiene)?; + let ast_id = self.source_ast_id_map.ast_id(m); + let res = MacroCall { path, ast_id }; + Some(id(self.data().macro_calls.alloc(res))) + } + + fn lower_macro_rules(&mut self, m: &ast::MacroRules) -> Option> { + let name = m.name().map(|it| it.as_name())?; + let attrs = Attrs::new(m, &self.hygiene); let ast_id = self.source_ast_id_map.ast_id(m); @@ -547,8 +557,8 @@ impl Ctx { }; let is_builtin = attrs.by_key("rustc_builtin_macro").exists(); - let res = MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id }; - Some(id(self.data().macro_calls.alloc(res))) + let res = MacroRules { name, is_export, is_builtin, is_local_inner, ast_id }; + Some(id(self.data().macro_rules.alloc(res))) } fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec { diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 19cd713ba..85cc342c4 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -11,7 +11,7 @@ use hir_expand::{ ast_id_map::FileAstId, builtin_derive::find_builtin_derive, builtin_macro::find_builtin_macro, - name::{name, AsName, Name}, + name::{AsName, Name}, proc_macro::ProcMacroExpander, HirFileId, MacroCallId, MacroDefId, MacroDefKind, }; @@ -25,7 +25,9 @@ use crate::{ attr::Attrs, db::DefDatabase, item_scope::{ImportType, PerNsGlobImports}, - item_tree::{self, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind}, + item_tree::{ + self, ItemTree, ItemTreeId, MacroCall, MacroRules, Mod, ModItem, ModKind, StructDefKind, + }, nameres::{ diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, @@ -972,7 +974,8 @@ impl ModCollector<'_, '_> { status: PartialResolvedImport::Unresolved, }) } - ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]), + ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]), + ModItem::MacroRules(mac) => self.collect_macro_rules(&self.item_tree[mac]), ModItem::Impl(imp) => { let module = ModuleId { krate: self.def_collector.def_map.krate, @@ -1276,45 +1279,37 @@ impl ModCollector<'_, '_> { self.def_collector.resolve_proc_macro(¯o_name); } - fn collect_macro(&mut self, mac: &MacroCall) { - let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); + fn collect_macro_rules(&mut self, mac: &MacroRules) { + let ast_id = InFile::new(self.file_id, mac.ast_id); - // Case 0: builtin macros + // Case 1: builtin macros if mac.is_builtin { - if let Some(name) = &mac.name { - let krate = self.def_collector.def_map.krate; - if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) { - self.def_collector.define_macro( - self.module_id, - name.clone(), - macro_id, - mac.is_export, - ); - return; - } - } - } - - // Case 1: macro rules, define a macro in crate-global mutable scope - if is_macro_rules(&mac.path) { - if let Some(name) = &mac.name { - let macro_id = MacroDefId { - ast_id: Some(ast_id.ast_id), - krate: Some(self.def_collector.def_map.krate), - kind: MacroDefKind::Declarative, - local_inner: mac.is_local_inner, - }; + 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.module_id, - name.clone(), + mac.name.clone(), macro_id, mac.is_export, ); + return; } - return; } - // Case 2: try to resolve in legacy scope and expand macro_rules + // Case 2: normal `macro_rules!` macro + let macro_id = MacroDefId { + ast_id: Some(ast_id), + krate: Some(self.def_collector.def_map.krate), + kind: MacroDefKind::Declarative, + local_inner: mac.is_local_inner, + }; + self.def_collector.define_macro(self.module_id, mac.name.clone(), macro_id, mac.is_export); + } + + fn collect_macro_call(&mut self, mac: &MacroCall) { + let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); + + // Case 1: try to resolve in legacy scope and expand macro_rules if let Some(macro_call_id) = ast_id.as_call_id(self.def_collector.db, self.def_collector.def_map.krate, |path| { path.as_ident().and_then(|name| { @@ -1332,7 +1327,7 @@ impl ModCollector<'_, '_> { return; } - // Case 3: resolve in module scope, expand during name resolution. + // Case 2: resolve in module scope, expand during name resolution. // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only. if ast_id.path.is_ident() { ast_id.path.kind = PathKind::Super(0); @@ -1370,10 +1365,6 @@ impl ModCollector<'_, '_> { } } -fn is_macro_rules(path: &ModPath) -> bool { - path.as_ident() == Some(&name![macro_rules]) -} - #[cfg(test)] mod tests { use crate::{db::DefDatabase, test_db::TestDB}; diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs index 60fa7646b..609925012 100644 --- a/crates/hir_def/src/path/lower.rs +++ b/crates/hir_def/src/path/lower.rs @@ -122,11 +122,9 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456 // We follow what it did anyway :) if segments.len() == 1 && kind == PathKind::Plain { - if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { - if macro_call.is_bang() { - if let Some(crate_id) = hygiene.local_inner_macros() { - kind = PathKind::DollarCrate(crate_id); - } + if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { + if let Some(crate_id) = hygiene.local_inner_macros() { + kind = PathKind::DollarCrate(crate_id); } } } diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index b1b432ded..bd9223825 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs @@ -63,7 +63,7 @@ macro_rules! register_builtin { pub fn find_builtin_macro( ident: &name::Name, krate: CrateId, - ast_id: AstId, + ast_id: AstId, ) -> Option { let kind = find_by_name(ident)?; @@ -515,12 +515,16 @@ mod tests { fn expand_builtin_macro(ra_fixture: &str) -> String { let (db, file_id) = TestDB::with_single_file(&ra_fixture); let parsed = db.parse(file_id); + let macro_rules: Vec<_> = + parsed.syntax_node().descendants().filter_map(ast::MacroRules::cast).collect(); let macro_calls: Vec<_> = parsed.syntax_node().descendants().filter_map(ast::MacroCall::cast).collect(); let ast_id_map = db.ast_id_map(file_id.into()); - let expander = find_by_name(¯o_calls[0].name().unwrap().as_name()).unwrap(); + assert_eq!(macro_rules.len(), 1, "test must contain exactly 1 `macro_rules!`"); + assert_eq!(macro_calls.len(), 1, "test must contain exactly 1 macro call"); + let expander = find_by_name(¯o_rules[0].name().unwrap().as_name()).unwrap(); let krate = CrateId(0); let file_id = match expander { @@ -528,7 +532,7 @@ mod tests { // the first one should be a macro_rules let def = MacroDefId { krate: Some(CrateId(0)), - ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0]))), + ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_rules[0]))), kind: MacroDefKind::BuiltIn(expander), local_inner: false, }; @@ -538,7 +542,7 @@ mod tests { krate, kind: MacroCallKind::FnLike(AstId::new( file_id.into(), - ast_id_map.ast_id(¯o_calls[1]), + ast_id_map.ast_id(¯o_calls[0]), )), }; @@ -549,12 +553,12 @@ mod tests { // the first one should be a macro_rules let def = MacroDefId { krate: Some(krate), - ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0]))), + ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_rules[0]))), kind: MacroDefKind::BuiltInEager(expander), local_inner: false, }; - let args = macro_calls[1].token_tree().unwrap(); + let args = macro_calls[0].token_tree().unwrap(); let parsed_args = mbe::ast_to_token_tree(&args).unwrap().0; let arg_id = db.intern_eager_expansion({ diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 1a9428514..ae3086a95 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs @@ -228,7 +228,7 @@ pub struct MacroDefId { // (which will probably require touching this code), we can instead use // that (and also remove the hacks for resolving built-in derives). pub krate: Option, - pub ast_id: Option>, + pub ast_id: Option>, pub kind: MacroDefKind, pub local_inner: bool, diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index 597a195d0..de97ec3c2 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs @@ -413,7 +413,6 @@ fn infer_local_macro() { expect![[r#" !0..6 '1usize': usize 10..89 '{ ...!(); }': () - 16..65 'macro_... }': {unknown} 74..76 '_a': usize "#]], ); diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs index 415795e8c..c51531391 100644 --- a/crates/ide/src/file_structure.rs +++ b/crates/ide/src/file_structure.rs @@ -150,13 +150,7 @@ fn structure_node(node: &SyntaxNode) -> Option { }; Some(node) }, - ast::MacroCall(it) => { - match it.path().and_then(|it| it.segment()).and_then(|it| it.name_ref()) { - Some(path_segment) if path_segment.text() == "macro_rules" - => decl(it), - _ => None, - } - }, + ast::MacroRules(it) => decl(it), _ => None, } } @@ -380,7 +374,7 @@ fn very_obsolete() {} label: "mc", navigation_range: 284..286, node_range: 271..303, - kind: MACRO_CALL, + kind: MACRO_RULES, detail: None, deprecated: false, }, @@ -389,7 +383,7 @@ fn very_obsolete() {} label: "mcexp", navigation_range: 334..339, node_range: 305..356, - kind: MACRO_CALL, + kind: MACRO_RULES, detail: None, deprecated: false, }, @@ -398,7 +392,7 @@ fn very_obsolete() {} label: "mcexp", navigation_range: 387..392, node_range: 358..409, - kind: MACRO_CALL, + kind: MACRO_RULES, detail: None, deprecated: false, }, diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 66f0f7950..675957fff 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -770,7 +770,7 @@ fn foo() { } "#, expect![[r#" - m1 MACRO_CALL FileId(0) 0..46 29..31 Other + m1 MACRO_RULES FileId(0) 0..46 29..31 Other FileId(0) 63..65 StructLiteral FileId(0) 73..75 StructLiteral diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index f5c6eabef..990b0f7d9 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -74,6 +74,7 @@ pub(crate) fn highlight( let mut stack = HighlightedRangeStack::new(); let mut current_macro_call: Option = None; + let mut current_macro_rules: Option = None; let mut format_string_highlighter = FormatStringHighlighter::default(); let mut macro_rules_highlighter = MacroRulesHighlighter::default(); let mut inside_attribute = false; @@ -106,28 +107,26 @@ pub(crate) fn highlight( binding_hash: None, }); } - if let Some(name) = mc.is_macro_rules() { - macro_rules_highlighter.init(); - if let Some((highlight, binding_hash)) = highlight_element( - &sema, - &mut bindings_shadow_count, - syntactic_name_ref_highlighting, - name.syntax().clone().into(), - ) { - stack.add(HighlightedRange { - range: name.syntax().text_range(), - highlight, - binding_hash, - }); - } - } current_macro_call = Some(mc.clone()); continue; } WalkEvent::Leave(Some(mc)) => { - assert!(current_macro_call == Some(mc)); + assert_eq!(current_macro_call, Some(mc)); current_macro_call = None; format_string_highlighter = FormatStringHighlighter::default(); + } + _ => (), + } + + match event.clone().map(|it| it.into_node().and_then(ast::MacroRules::cast)) { + WalkEvent::Enter(Some(mac)) => { + macro_rules_highlighter.init(); + current_macro_rules = Some(mac); + continue; + } + WalkEvent::Leave(Some(mac)) => { + assert_eq!(current_macro_rules, Some(mac)); + current_macro_rules = None; macro_rules_highlighter = MacroRulesHighlighter::default(); } _ => (), @@ -163,6 +162,12 @@ pub(crate) fn highlight( let range = element.text_range(); + if current_macro_rules.is_some() { + if let Some(tok) = element.as_token() { + macro_rules_highlighter.advance(tok); + } + } + let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT { // Inside a macro -- expand it first let token = match element.clone().into_token() { @@ -173,9 +178,6 @@ pub(crate) fn highlight( let parent = token.parent(); format_string_highlighter.check_for_format_string(&parent); - if let Some(tok) = element.as_token() { - macro_rules_highlighter.advance(tok); - } // We only care Name and Name_ref match (token.kind(), parent.kind()) { @@ -386,10 +388,14 @@ impl HighlightedRangeStack { let mut res = self.stack.pop().unwrap(); res.sort_by_key(|range| range.range.start()); // Check that ranges are sorted and disjoint - assert!(res - .iter() - .zip(res.iter().skip(1)) - .all(|(left, right)| left.range.end() <= right.range.start())); + for (left, right) in res.iter().zip(res.iter().skip(1)) { + assert!( + left.range.end() <= right.range.start(), + "left: {:#?}, right: {:#?}", + left, + right + ); + } res } } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 396fd46fb..920956b51 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -95,7 +95,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// ``` /// noop!(1); /// ``` -macro_rules! noop { +macro_rules! noop { ($expr:expr) => { $expr } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index d398e1ec8..c843b5085 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -35,13 +35,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
macro_rules! println {
+
macro_rules! println {
     ($($arg:tt)*) => ({
         $crate::io::_print($crate::format_args_nl!($($arg)*));
     })
 }
-#[rustc_builtin_macro]
-macro_rules! format_args_nl {
+#[rustc_builtin_macro]
+macro_rules! format_args_nl {
     ($fmt:expr) => {{ /* compiler built-in */ }};
     ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
 }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index a3b4f20e8..0569cf1e5 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -125,7 +125,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     let bar = foobar();
 }
 
-macro_rules! def_fn {
+macro_rules! def_fn {
     ($($tt:tt)*) => {$($tt)*}
 }
 
@@ -135,13 +135,13 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     }
 }
 
-macro_rules! noop {
+macro_rules! noop {
     ($expr:expr) => {
         $expr
     }
 }
 
-macro_rules! keyword_frag {
+macro_rules! keyword_frag {
     ($type:ty) => ($type)
 }
 
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index 5d2cd30d1..d4a774261 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -217,7 +217,7 @@ impl NameClass {
                     let def: hir::TypeAlias = sema.to_def(&it)?;
                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
                 },
-                ast::MacroCall(it) => {
+                ast::MacroRules(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 654df898e..121063aea 100644
--- a/crates/ide_db/src/symbol_index.rs
+++ b/crates/ide_db/src/symbol_index.rs
@@ -404,13 +404,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::MacroCall(it) => {
-                if it.is_macro_rules().is_some() {
-                    decl(it)
-                } else {
-                    None
-                }
-            },
+            ast::MacroRules(it) => decl(it),
             _ => None,
         }
     }
diff --git a/crates/mbe/src/mbe_expander.rs b/crates/mbe/src/mbe_expander.rs
index 97bce0536..a80b73db4 100644
--- a/crates/mbe/src/mbe_expander.rs
+++ b/crates/mbe/src/mbe_expander.rs
@@ -163,7 +163,7 @@ mod tests {
     fn create_rules(macro_definition: &str) -> crate::MacroRules {
         let source_file = ast::SourceFile::parse(macro_definition).ok().unwrap();
         let macro_definition =
-            source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
+            source_file.syntax().descendants().find_map(ast::MacroRules::cast).unwrap();
 
         let (definition_tt, _) =
             ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap();
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 843054fe8..dff6e98c2 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -48,7 +48,7 @@ mod rule_parsing {
         let macro_definition = format!(" macro_rules! m {{ {} }} ", arm_definition);
         let source_file = ast::SourceFile::parse(¯o_definition).ok().unwrap();
         let macro_definition =
-            source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
+            source_file.syntax().descendants().find_map(ast::MacroRules::cast).unwrap();
 
         let (definition_tt, _) =
             ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap();
@@ -1668,7 +1668,7 @@ impl MacroFixture {
 fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree {
     let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap();
     let macro_definition =
-        source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
+        source_file.syntax().descendants().find_map(ast::MacroRules::cast).unwrap();
 
     let (definition_tt, _) = ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap();
 
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index ad29b82f7..8999829b4 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -232,6 +232,9 @@ fn items_without_modifiers(p: &mut Parser, m: Marker) -> Result<(), Marker> {
         T![macro] => {
             macro_def(p, m);
         }
+        IDENT if p.at_contextual_kw("macro_rules") && p.nth(1) == BANG => {
+            macro_rules(p, m);
+        }
         IDENT if p.at_contextual_kw("union") && p.nth(1) == IDENT => {
             // test union_items
             // union Foo {}
@@ -363,6 +366,34 @@ pub(crate) fn item_list(p: &mut Parser) {
     m.complete(p, ITEM_LIST);
 }
 
+fn macro_rules(p: &mut Parser, m: Marker) {
+    assert!(p.at_contextual_kw("macro_rules"));
+    p.bump_remap(T![macro_rules]);
+    p.expect(T![!]);
+
+    if p.at(IDENT) {
+        name(p);
+    }
+    // Special-case `macro_rules! try`.
+    // This is a hack until we do proper edition support
+
+    // test try_macro_rules
+    // macro_rules! try { () => {} }
+    if p.at(T![try]) {
+        let m = p.start();
+        p.bump_remap(IDENT);
+        m.complete(p, NAME);
+    }
+
+    match p.current() {
+        T!['{'] => {
+            token_tree(p);
+        }
+        _ => p.error("expected `{`"),
+    }
+    m.complete(p, MACRO_RULES);
+}
+
 // test macro_def
 // macro m { ($i:ident) => {} }
 // macro m($i:ident) {}
@@ -394,19 +425,6 @@ fn macro_call(p: &mut Parser) -> BlockLike {
 
 pub(super) fn macro_call_after_excl(p: &mut Parser) -> BlockLike {
     p.expect(T![!]);
-    if p.at(IDENT) {
-        name(p);
-    }
-    // Special-case `macro_rules! try`.
-    // This is a hack until we do proper edition support
-
-    // test try_macro_rules
-    // macro_rules! try { () => {} }
-    if p.at(T![try]) {
-        let m = p.start();
-        p.bump_remap(IDENT);
-        m.complete(p, NAME);
-    }
 
     match p.current() {
         T!['{'] => {
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 8bc6688f3..5d6ec17a4 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -106,6 +106,7 @@ pub enum SyntaxKind {
     EXISTENTIAL_KW,
     UNION_KW,
     RAW_KW,
+    MACRO_RULES_KW,
     INT_NUMBER,
     FLOAT_NUMBER,
     CHAR,
@@ -135,6 +136,8 @@ pub enum SyntaxKind {
     IMPL,
     TYPE_ALIAS,
     MACRO_CALL,
+    MACRO_RULES,
+    MACRO_ARM,
     TOKEN_TREE,
     MACRO_DEF,
     PAREN_TYPE,
@@ -258,7 +261,7 @@ impl SyntaxKind {
             | IMPL_KW | IN_KW | LET_KW | LOOP_KW | MACRO_KW | MATCH_KW | MOD_KW | MOVE_KW
             | MUT_KW | PUB_KW | REF_KW | RETURN_KW | SELF_KW | STATIC_KW | STRUCT_KW | SUPER_KW
             | TRAIT_KW | TRUE_KW | TRY_KW | TYPE_KW | UNSAFE_KW | USE_KW | WHERE_KW | WHILE_KW
-            | AUTO_KW | DEFAULT_KW | EXISTENTIAL_KW | UNION_KW | RAW_KW => true,
+            | AUTO_KW | DEFAULT_KW | EXISTENTIAL_KW | UNION_KW | RAW_KW | MACRO_RULES_KW => true,
             _ => false,
         }
     }
@@ -361,4 +364,4 @@ impl SyntaxKind {
     }
 }
 #[macro_export]
-macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [lifetime] => { $ crate :: SyntaxKind :: LIFETIME } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
+macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [lifetime] => { $ crate :: SyntaxKind :: LIFETIME } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 30d11b146..0ad75214f 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -128,7 +128,6 @@ pub struct MacroCall {
     pub(crate) syntax: SyntaxNode,
 }
 impl ast::AttrsOwner for MacroCall {}
-impl ast::NameOwner for MacroCall {}
 impl MacroCall {
     pub fn path(&self) -> Option { support::child(&self.syntax) }
     pub fn excl_token(&self) -> Option { support::token(&self.syntax, T![!]) }
@@ -273,6 +272,20 @@ impl Impl {
     pub fn assoc_item_list(&self) -> Option { support::child(&self.syntax) }
 }
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct MacroRules {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ast::AttrsOwner for MacroRules {}
+impl ast::NameOwner for MacroRules {}
+impl ast::VisibilityOwner for MacroRules {}
+impl MacroRules {
+    pub fn macro_rules_token(&self) -> Option {
+        support::token(&self.syntax, T![macro_rules])
+    }
+    pub fn excl_token(&self) -> Option { support::token(&self.syntax, T![!]) }
+    pub fn token_tree(&self) -> Option { support::child(&self.syntax) }
+}
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Module {
     pub(crate) syntax: SyntaxNode,
 }
@@ -1318,6 +1331,7 @@ pub enum Item {
     Fn(Fn),
     Impl(Impl),
     MacroCall(MacroCall),
+    MacroRules(MacroRules),
     Module(Module),
     Static(Static),
     Struct(Struct),
@@ -1374,7 +1388,6 @@ pub enum AssocItem {
     TypeAlias(TypeAlias),
 }
 impl ast::AttrsOwner for AssocItem {}
-impl ast::NameOwner for AssocItem {}
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum ExternItem {
     Fn(Fn),
@@ -1383,7 +1396,6 @@ pub enum ExternItem {
     TypeAlias(TypeAlias),
 }
 impl ast::AttrsOwner for ExternItem {}
-impl ast::NameOwner for ExternItem {}
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum GenericParam {
     ConstParam(ConstParam),
@@ -1666,6 +1678,17 @@ impl AstNode for Impl {
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl AstNode for MacroRules {
+    fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_RULES }
+    fn cast(syntax: SyntaxNode) -> Option {
+        if Self::can_cast(syntax.kind()) {
+            Some(Self { syntax })
+        } else {
+            None
+        }
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
 impl AstNode for Module {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MODULE }
     fn cast(syntax: SyntaxNode) -> Option {
@@ -3060,6 +3083,9 @@ impl From for Item {
 impl From for Item {
     fn from(node: MacroCall) -> Item { Item::MacroCall(node) }
 }
+impl From for Item {
+    fn from(node: MacroRules) -> Item { Item::MacroRules(node) }
+}
 impl From for Item {
     fn from(node: Module) -> Item { Item::Module(node) }
 }
@@ -3084,8 +3110,8 @@ impl From for Item {
 impl AstNode for Item {
     fn can_cast(kind: SyntaxKind) -> bool {
         match kind {
-            CONST | ENUM | EXTERN_BLOCK | EXTERN_CRATE | FN | IMPL | MACRO_CALL | MODULE
-            | STATIC | STRUCT | TRAIT | TYPE_ALIAS | UNION | USE => true,
+            CONST | ENUM | EXTERN_BLOCK | EXTERN_CRATE | FN | IMPL | MACRO_CALL | MACRO_RULES
+            | MODULE | STATIC | STRUCT | TRAIT | TYPE_ALIAS | UNION | USE => true,
             _ => false,
         }
     }
@@ -3098,6 +3124,7 @@ impl AstNode for Item {
             FN => Item::Fn(Fn { syntax }),
             IMPL => Item::Impl(Impl { syntax }),
             MACRO_CALL => Item::MacroCall(MacroCall { syntax }),
+            MACRO_RULES => Item::MacroRules(MacroRules { syntax }),
             MODULE => Item::Module(Module { syntax }),
             STATIC => Item::Static(Static { syntax }),
             STRUCT => Item::Struct(Struct { syntax }),
@@ -3118,6 +3145,7 @@ impl AstNode for Item {
             Item::Fn(it) => &it.syntax,
             Item::Impl(it) => &it.syntax,
             Item::MacroCall(it) => &it.syntax,
+            Item::MacroRules(it) => &it.syntax,
             Item::Module(it) => &it.syntax,
             Item::Static(it) => &it.syntax,
             Item::Struct(it) => &it.syntax,
@@ -3582,6 +3610,11 @@ impl std::fmt::Display for Impl {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
+impl std::fmt::Display for MacroRules {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(self.syntax(), f)
+    }
+}
 impl std::fmt::Display for Module {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 820af2d20..c59a29eab 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -382,21 +382,6 @@ impl ast::Visibility {
     }
 }
 
-impl ast::MacroCall {
-    pub fn is_macro_rules(&self) -> Option {
-        let name_ref = self.path()?.segment()?.name_ref()?;
-        if name_ref.text() == "macro_rules" {
-            self.name()
-        } else {
-            None
-        }
-    }
-
-    pub fn is_bang(&self) -> bool {
-        self.is_macro_rules().is_none()
-    }
-}
-
 impl ast::LifetimeParam {
     pub fn lifetime_bounds(&self) -> impl Iterator {
         self.syntax()
@@ -476,5 +461,5 @@ impl ast::DocCommentsOwner for ast::Static {}
 impl ast::DocCommentsOwner for ast::Const {}
 impl ast::DocCommentsOwner for ast::TypeAlias {}
 impl ast::DocCommentsOwner for ast::Impl {}
-impl ast::DocCommentsOwner for ast::MacroCall {}
+impl ast::DocCommentsOwner for ast::MacroRules {}
 impl ast::DocCommentsOwner for ast::Use {}
diff --git a/crates/syntax/src/display.rs b/crates/syntax/src/display.rs
index 8d2c7eae4..d33bde30c 100644
--- a/crates/syntax/src/display.rs
+++ b/crates/syntax/src/display.rs
@@ -76,7 +76,7 @@ pub fn type_label(node: &ast::TypeAlias) -> String {
     label.trim().to_owned()
 }
 
-pub fn macro_label(node: &ast::MacroCall) -> String {
+pub fn macro_label(node: &ast::MacroRules) -> String {
     let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default();
     let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" };
     format!("{}macro_rules! {}", vis, name)
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs
index 49842177a..ce27c3dd9 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,
 ) -> usize {
     match kind {
-        MACRO_CALL | CONST | TYPE_ALIAS | STRUCT | ENUM | VARIANT | FN | TRAIT | MODULE
-        | RECORD_FIELD | STATIC | USE => {
+        MACRO_CALL | MACRO_RULES | CONST | TYPE_ALIAS | STRUCT | ENUM | VARIANT | FN | TRAIT
+        | MODULE | RECORD_FIELD | STATIC | USE => {
             let mut res = 0;
             let mut trivias = trivias.enumerate().peekable();
 
diff --git a/crates/syntax/test_data/parser/err/0002_duplicate_shebang.rast b/crates/syntax/test_data/parser/err/0002_duplicate_shebang.rast
index 4cfd1bce4..9ad5b12b8 100644
--- a/crates/syntax/test_data/parser/err/0002_duplicate_shebang.rast
+++ b/crates/syntax/test_data/parser/err/0002_duplicate_shebang.rast
@@ -17,14 +17,17 @@ SOURCE_FILE@0..42
           IDENT@28..31 "bin"
   ERROR@31..32
     SLASH@31..32 "/"
-  MACRO_CALL@32..41
+  MACRO_CALL@32..35
     PATH@32..35
       PATH_SEGMENT@32..35
         NAME_REF@32..35
           IDENT@32..35 "env"
-    WHITESPACE@35..36 " "
-    NAME@36..41
-      IDENT@36..41 "rusti"
+  WHITESPACE@35..36 " "
+  MACRO_CALL@36..41
+    PATH@36..41
+      PATH_SEGMENT@36..41
+        NAME_REF@36..41
+          IDENT@36..41 "rusti"
   WHITESPACE@41..42 "\n"
 error 23..23: expected `[`
 error 23..23: expected an item
@@ -35,5 +38,8 @@ error 31..31: expected `{`, `[`, `(`
 error 31..31: expected SEMICOLON
 error 31..31: expected an item
 error 35..35: expected BANG
+error 35..35: expected `{`, `[`, `(`
+error 35..35: expected SEMICOLON
+error 41..41: expected BANG
 error 41..41: expected `{`, `[`, `(`
 error 41..41: expected SEMICOLON
diff --git a/crates/syntax/test_data/parser/inline/ok/0062_mod_contents.rast b/crates/syntax/test_data/parser/inline/ok/0062_mod_contents.rast
index de8217064..e4fb32de1 100644
--- a/crates/syntax/test_data/parser/inline/ok/0062_mod_contents.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0062_mod_contents.rast
@@ -12,11 +12,8 @@ SOURCE_FILE@0..70
       L_CURLY@9..10 "{"
       R_CURLY@10..11 "}"
   WHITESPACE@11..12 "\n"
-  MACRO_CALL@12..31
-    PATH@12..23
-      PATH_SEGMENT@12..23
-        NAME_REF@12..23
-          IDENT@12..23 "macro_rules"
+  MACRO_RULES@12..31
+    MACRO_RULES_KW@12..23 "macro_rules"
     BANG@23..24 "!"
     WHITESPACE@24..25 " "
     NAME@25..28
diff --git a/crates/syntax/test_data/parser/inline/ok/0096_no_semi_after_block.rast b/crates/syntax/test_data/parser/inline/ok/0096_no_semi_after_block.rast
index e757249f0..e84b9164f 100644
--- a/crates/syntax/test_data/parser/inline/ok/0096_no_semi_after_block.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0096_no_semi_after_block.rast
@@ -82,32 +82,28 @@ SOURCE_FILE@0..167
           L_CURLY@102..103 "{"
           R_CURLY@103..104 "}"
       WHITESPACE@104..109 "\n    "
-      EXPR_STMT@109..152
-        MACRO_CALL@109..152
-          PATH@109..120
-            PATH_SEGMENT@109..120
-              NAME_REF@109..120
-                IDENT@109..120 "macro_rules"
-          BANG@120..121 "!"
-          WHITESPACE@121..122 " "
-          NAME@122..126
-            IDENT@122..126 "test"
-          WHITESPACE@126..127 " "
-          TOKEN_TREE@127..152
-            L_CURLY@127..128 "{"
-            WHITESPACE@128..138 "\n         "
-            TOKEN_TREE@138..140
-              L_PAREN@138..139 "("
-              R_PAREN@139..140 ")"
-            WHITESPACE@140..141 " "
-            EQ@141..142 "="
-            R_ANGLE@142..143 ">"
-            WHITESPACE@143..144 " "
-            TOKEN_TREE@144..146
-              L_CURLY@144..145 "{"
-              R_CURLY@145..146 "}"
-            WHITESPACE@146..151 "\n    "
-            R_CURLY@151..152 "}"
+      MACRO_RULES@109..152
+        MACRO_RULES_KW@109..120 "macro_rules"
+        BANG@120..121 "!"
+        WHITESPACE@121..122 " "
+        NAME@122..126
+          IDENT@122..126 "test"
+        WHITESPACE@126..127 " "
+        TOKEN_TREE@127..152
+          L_CURLY@127..128 "{"
+          WHITESPACE@128..138 "\n         "
+          TOKEN_TREE@138..140
+            L_PAREN@138..139 "("
+            R_PAREN@139..140 ")"
+          WHITESPACE@140..141 " "
+          EQ@141..142 "="
+          R_ANGLE@142..143 ">"
+          WHITESPACE@143..144 " "
+          TOKEN_TREE@144..146
+            L_CURLY@144..145 "{"
+            R_CURLY@145..146 "}"
+          WHITESPACE@146..151 "\n    "
+          R_CURLY@151..152 "}"
       WHITESPACE@152..157 "\n    "
       MACRO_CALL@157..164
         PATH@157..161
diff --git a/crates/syntax/test_data/parser/inline/ok/0160_try_macro_rules.rast b/crates/syntax/test_data/parser/inline/ok/0160_try_macro_rules.rast
index 05b89d1c3..d1c22947b 100644
--- a/crates/syntax/test_data/parser/inline/ok/0160_try_macro_rules.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0160_try_macro_rules.rast
@@ -1,9 +1,6 @@
 SOURCE_FILE@0..30
-  MACRO_CALL@0..29
-    PATH@0..11
-      PATH_SEGMENT@0..11
-        NAME_REF@0..11
-          IDENT@0..11 "macro_rules"
+  MACRO_RULES@0..29
+    MACRO_RULES_KW@0..11 "macro_rules"
     BANG@11..12 "!"
     WHITESPACE@12..13 " "
     NAME@13..16
diff --git a/crates/syntax/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast b/crates/syntax/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast
index be60f7a8e..87d8ebcba 100644
--- a/crates/syntax/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast
+++ b/crates/syntax/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast
@@ -1,5 +1,5 @@
 SOURCE_FILE@0..65
-  MACRO_CALL@0..64
+  MACRO_RULES@0..64
     COMMENT@0..13 "/// Some docs"
     WHITESPACE@13..14 "\n"
     ATTR@14..29
@@ -11,10 +11,7 @@ SOURCE_FILE@0..65
             IDENT@16..28 "macro_export"
       R_BRACK@28..29 "]"
     WHITESPACE@29..30 "\n"
-    PATH@30..41
-      PATH_SEGMENT@30..41
-        NAME_REF@30..41
-          IDENT@30..41 "macro_rules"
+    MACRO_RULES_KW@30..41 "macro_rules"
     BANG@41..42 "!"
     WHITESPACE@42..43 " "
     NAME@43..46
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs
index 8ceaaf60e..72a4c10f5 100644
--- a/xtask/src/ast_src.rs
+++ b/xtask/src/ast_src.rs
@@ -70,7 +70,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
         "match", "mod", "move", "mut", "pub", "ref", "return", "self", "static", "struct", "super",
         "trait", "true", "try", "type", "unsafe", "use", "where", "while",
     ],
-    contextual_keywords: &["auto", "default", "existential", "union", "raw"],
+    contextual_keywords: &["auto", "default", "existential", "union", "raw", "macro_rules"],
     literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING"],
     tokens: &[
         "ERROR",
@@ -98,6 +98,8 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
         "IMPL",
         "TYPE_ALIAS",
         "MACRO_CALL",
+        "MACRO_RULES",
+        "MACRO_ARM",
         "TOKEN_TREE",
         "MACRO_DEF",
         "PAREN_TYPE",
-- 
cgit v1.2.3


From 479babf8740a4d3cf6fc03a5f4a2fca00d387501 Mon Sep 17 00:00:00 2001
From: Jonas Schievink 
Date: Tue, 15 Dec 2020 15:37:49 +0100
Subject: Reject visibilities on `macro_rules!`

---
 crates/syntax/src/validation.rs | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index 6f45149bf..2ddaeb176 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -3,7 +3,9 @@
 mod block;
 
 use crate::{
-    algo, ast, match_ast, AstNode, SyntaxError,
+    algo,
+    ast::{self, VisibilityOwner},
+    match_ast, AstNode, SyntaxError,
     SyntaxKind::{CONST, FN, INT_NUMBER, TYPE_ALIAS},
     SyntaxNode, SyntaxToken, TextSize, T,
 };
@@ -99,6 +101,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec {
                 ast::RefType(it) => validate_trait_object_ref_ty(it, &mut errors),
                 ast::PtrType(it) => validate_trait_object_ptr_ty(it, &mut errors),
                 ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, &mut errors),
+                ast::MacroRules(it) => validate_macro_rules(it, &mut errors),
                 _ => (),
             }
         }
@@ -350,3 +353,12 @@ fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option {
     }
     None
 }
+
+fn validate_macro_rules(mac: ast::MacroRules, errors: &mut Vec) {
+    if let Some(vis) = mac.visibility() {
+        errors.push(SyntaxError::new(
+            "visibilities are not allowed on `macro_rules!` items",
+            vis.syntax().text_range(),
+        ));
+    }
+}
-- 
cgit v1.2.3