diff --git a/Cargo.lock b/Cargo.lock index ff2c33f45d1..e44b7d1d920 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1827,9 +1827,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] name = "ungrammar" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873186a460627379e7e28880a0d33b729c205634f6f021321f50b323235e62d7" +checksum = "7311ee93faac43aa9da26b043eb244092a29a3078c79af9396f63f800cc3a59a" [[package]] name = "unicase" diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 42dc35b762c..9bfcd215a5e 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs @@ -970,7 +970,7 @@ impl MacroDef { /// defines this macro. The reasons for this is that macros are expanded /// early, in `hir_expand`, where modules simply do not exist yet. pub fn module(self, db: &dyn HirDatabase) -> Option { - let krate = self.id.krate?; + let krate = self.id.krate; let module_id = db.crate_def_map(krate).root; Some(Module::new(Crate { id: krate }, module_id)) } diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs index 04845037feb..11ae63c31ca 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::MacroRules; - fn source(self, db: &dyn HirDatabase) -> InFile { + type Ast = ast::Macro; + 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/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index a333d7aea9d..3efca5baa5c 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -157,8 +157,8 @@ impl SourceToDefCtx<'_, '_> { let file_id = src.file_id.original_file(self.db.upcast()); let krate = self.file_to_def(file_id)?.krate; let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value); - let ast_id = Some(AstId::new(src.file_id, file_ast_id)); - Some(MacroDefId { krate: Some(krate), ast_id, kind, local_inner: false }) + let ast_id = Some(AstId::new(src.file_id, file_ast_id.upcast())); + Some(MacroDefId { krate, ast_id, kind, local_inner: false }) } pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option { diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index bdba4c33ea4..23e2fd7641f 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -772,7 +772,10 @@ impl ExprCollector<'_> { | ast::Item::Module(_) | ast::Item::MacroCall(_) => return None, ast::Item::MacroRules(def) => { - return Some(Either::Right(def)); + return Some(Either::Right(ast::Macro::from(def))); + } + ast::Item::MacroDef(def) => { + return Some(Either::Right(ast::Macro::from(def))); } }; @@ -800,7 +803,7 @@ impl ExprCollector<'_> { } Either::Right(e) => { let mac = MacroDefId { - krate: Some(self.expander.module.krate), + krate: self.expander.module.krate, ast_id: Some(self.expander.ast_id(&e)), kind: MacroDefKind::Declarative, local_inner: false, diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index a8b3fe844a5..62ab3b2bd62 100644 --- a/crates/hir_def/src/item_scope.rs +++ b/crates/hir_def/src/item_scope.rs @@ -363,7 +363,7 @@ impl ItemInNs { ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate, ModuleDefId::BuiltinType(_) => return None, }, - ItemInNs::Macros(id) => return id.krate, + ItemInNs::Macros(id) => return Some(id.krate), }) } } diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 1c9babf3712..8cd0b18ccda 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs @@ -143,6 +143,7 @@ impl ItemTree { mods, macro_calls, macro_rules, + macro_defs, exprs, vis, generics, @@ -164,6 +165,7 @@ impl ItemTree { mods.shrink_to_fit(); macro_calls.shrink_to_fit(); macro_rules.shrink_to_fit(); + macro_defs.shrink_to_fit(); exprs.shrink_to_fit(); vis.arena.shrink_to_fit(); @@ -283,6 +285,7 @@ struct ItemTreeData { mods: Arena, macro_calls: Arena, macro_rules: Arena, + macro_defs: Arena, exprs: Arena, vis: ItemVisibilities, @@ -431,6 +434,7 @@ mod_items! { Mod in mods -> ast::Module, MacroCall in macro_calls -> ast::MacroCall, MacroRules in macro_rules -> ast::MacroRules, + MacroDef in macro_defs -> ast::MacroDef, } macro_rules! impl_index { @@ -640,7 +644,7 @@ pub struct MacroCall { #[derive(Debug, Clone, Eq, PartialEq)] pub struct MacroRules { - /// For `macro_rules!` declarations, this is the name of the declared macro. + /// The name of the declared macro. pub name: Name, /// Has `#[macro_export]`. pub is_export: bool, @@ -651,6 +655,16 @@ pub struct MacroRules { pub ast_id: FileAstId, } +/// "Macros 2.0" macro definition. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct MacroDef { + pub name: Name, + pub visibility: RawVisibilityId, + /// Has `#[rustc_builtin_macro]`. + pub is_builtin: bool, + pub ast_id: FileAstId, +} + // NB: There's no `FileAstId` for `Expr`. The only case where this would be useful is for array // lengths, but we don't do much with them yet. #[derive(Debug, Clone, Eq, PartialEq)] @@ -680,7 +694,8 @@ impl ModItem { | ModItem::Trait(_) | ModItem::Impl(_) | ModItem::Mod(_) - | ModItem::MacroRules(_) => None, + | ModItem::MacroRules(_) + | ModItem::MacroDef(_) => None, ModItem::MacroCall(call) => Some(AssocItem::MacroCall(*call)), ModItem::Const(konst) => Some(AssocItem::Const(*konst)), ModItem::TypeAlias(alias) => Some(AssocItem::TypeAlias(*alias)), @@ -708,6 +723,7 @@ impl ModItem { 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(), + ModItem::MacroDef(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 b39d7fb7ac1..1dc06a21155 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs @@ -101,7 +101,8 @@ impl Ctx { | ast::Item::ExternCrate(_) | ast::Item::Use(_) | ast::Item::MacroCall(_) - | ast::Item::MacroRules(_) => {} + | ast::Item::MacroRules(_) + | ast::Item::MacroDef(_) => {} }; let attrs = Attrs::new(item, &self.hygiene); @@ -122,6 +123,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::MacroDef(ast) => self.lower_macro_def(ast).map(Into::into), ast::Item::ExternBlock(ast) => { Some(ModItems(self.lower_extern_block(ast).into_iter().collect::>())) } @@ -561,6 +563,18 @@ impl Ctx { Some(id(self.data().macro_rules.alloc(res))) } + fn lower_macro_def(&mut self, m: &ast::MacroDef) -> 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); + let visibility = self.lower_visibility(m); + + let is_builtin = attrs.by_key("rustc_builtin_macro").exists(); + let res = MacroDef { name, is_builtin, ast_id, visibility }; + Some(id(self.data().macro_defs.alloc(res))) + } + fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec { block.extern_item_list().map_or(Vec::new(), |list| { list.extern_items() diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 85cc342c446..785895277fa 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -309,13 +309,13 @@ impl DefCollector<'_> { let macro_def = match self.proc_macros.iter().find(|(n, _)| n == name) { Some((_, expander)) => MacroDefId { ast_id: None, - krate: Some(self.def_map.krate), + krate: self.def_map.krate, kind: MacroDefKind::ProcMacro(*expander), local_inner: false, }, None => MacroDefId { ast_id: None, - krate: Some(self.def_map.krate), + krate: self.def_map.krate, kind: MacroDefKind::ProcMacro(ProcMacroExpander::dummy(self.def_map.krate)), local_inner: false, }, @@ -784,14 +784,6 @@ impl DefCollector<'_> { directive: &DeriveDirective, path: &ModPath, ) -> Option { - if let Some(name) = path.as_ident() { - // FIXME this should actually be handled with the normal name - // resolution; the std lib defines built-in stubs for the derives, - // but these are new-style `macro`s, which we don't support yet - if let Some(def_id) = find_builtin_derive(name) { - return Some(def_id); - } - } let resolved_res = self.def_map.resolve_path_fp_with_macro( self.db, ResolveMode::Other, @@ -976,6 +968,35 @@ impl ModCollector<'_, '_> { } ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]), ModItem::MacroRules(mac) => self.collect_macro_rules(&self.item_tree[mac]), + 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. + if mac.is_builtin { + 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::Impl(imp) => { let module = ModuleId { krate: self.def_collector.def_map.krate, @@ -1280,7 +1301,7 @@ impl ModCollector<'_, '_> { } fn collect_macro_rules(&mut self, mac: &MacroRules) { - let ast_id = InFile::new(self.file_id, mac.ast_id); + let ast_id = InFile::new(self.file_id, mac.ast_id.upcast()); // Case 1: builtin macros if mac.is_builtin { @@ -1299,7 +1320,7 @@ impl ModCollector<'_, '_> { // Case 2: normal `macro_rules!` macro let macro_id = MacroDefId { ast_id: Some(ast_id), - krate: Some(self.def_collector.def_map.krate), + krate: self.def_collector.def_map.krate, kind: MacroDefKind::Declarative, local_inner: mac.is_local_inner, }; diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index 305fca0f9c1..6fe2ee78a1f 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs @@ -633,14 +633,43 @@ pub struct bar; fn expand_derive() { let map = compute_crate_def_map( " - //- /main.rs + //- /main.rs crate:main deps:core + use core::*; + #[derive(Copy, Clone)] struct Foo; + + //- /core.rs crate:core + #[rustc_builtin_macro] + pub macro Copy {} + + #[rustc_builtin_macro] + pub macro Clone {} ", ); assert_eq!(map.modules[map.root].scope.impls().len(), 2); } +#[test] +fn resolve_builtin_derive() { + check( + r#" +//- /main.rs crate:main deps:core +use core::*; + +//- /core.rs crate:core +#[rustc_builtin_macro] +pub macro Clone {} + +pub trait Clone {} +"#, + expect![[r#" + crate + Clone: t m + "#]], + ); +} + #[test] fn macro_expansion_overflow() { mark::check!(macro_expansion_overflow); diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs index 988a60d56ad..ad378762a1e 100644 --- a/crates/hir_expand/src/builtin_derive.rs +++ b/crates/hir_expand/src/builtin_derive.rs @@ -8,7 +8,7 @@ use syntax::{ match_ast, }; -use crate::{db::AstDatabase, name, quote, LazyMacroId, MacroDefId, MacroDefKind}; +use crate::{db::AstDatabase, name, quote, AstId, CrateId, LazyMacroId, MacroDefId, MacroDefKind}; macro_rules! register_builtin { ( $($trait:ident => $expand:ident),* ) => { @@ -29,16 +29,15 @@ macro_rules! register_builtin { }; expander(db, id, tt) } + + fn find_by_name(name: &name::Name) -> Option { + match name { + $( id if id == &name::name![$trait] => Some(BuiltinDeriveExpander::$trait), )* + _ => None, + } + } } - pub fn find_builtin_derive(ident: &name::Name) -> Option { - let kind = match ident { - $( id if id == &name::name![$trait] => BuiltinDeriveExpander::$trait, )* - _ => return None, - }; - - Some(MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(kind), local_inner: false }) - } }; } @@ -54,6 +53,20 @@ register_builtin! { PartialEq => partial_eq_expand } +pub fn find_builtin_derive( + ident: &name::Name, + krate: CrateId, + ast_id: AstId, +) -> Option { + let expander = BuiltinDeriveExpander::find_by_name(ident)?; + Some(MacroDefId { + krate, + ast_id: Some(ast_id), + kind: MacroDefKind::BuiltInDerive(expander), + local_inner: false, + }) +} + struct BasicAdtInfo { name: tt::Ident, type_params: usize, @@ -261,7 +274,7 @@ mod tests { use super::*; fn expand_builtin_derive(s: &str, name: Name) -> String { - let def = find_builtin_derive(&name).unwrap(); + let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap(); let fixture = format!( r#"//- /main.rs crate:main deps:core <|> @@ -283,7 +296,12 @@ mod tests { let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0])); let loc = MacroCallLoc { - def, + def: MacroDefId { + krate: CrateId(0), + ast_id: None, + kind: MacroDefKind::BuiltInDerive(expander), + local_inner: false, + }, krate: CrateId(0), kind: MacroCallKind::Attr(attr_id, name.to_string()), }; diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index bd92238257c..dddbbcdac48 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs @@ -63,19 +63,19 @@ 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)?; match kind { Either::Left(kind) => Some(MacroDefId { - krate: Some(krate), + krate, ast_id: Some(ast_id), kind: MacroDefKind::BuiltIn(kind), local_inner: false, }), Either::Right(kind) => Some(MacroDefId { - krate: Some(krate), + krate, ast_id: Some(ast_id), kind: MacroDefKind::BuiltInEager(kind), local_inner: false, @@ -515,24 +515,27 @@ 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<_> = + let mut macro_rules: Vec<_> = parsed.syntax_node().descendants().filter_map(ast::MacroRules::cast).collect(); - let macro_calls: Vec<_> = + let mut macro_calls: Vec<_> = parsed.syntax_node().descendants().filter_map(ast::MacroCall::cast).collect(); let ast_id_map = db.ast_id_map(file_id.into()); 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 macro_rules = ast::Macro::from(macro_rules.pop().unwrap()); + let macro_call = macro_calls.pop().unwrap(); + + let expander = find_by_name(¯o_rules.name().unwrap().as_name()).unwrap(); let krate = CrateId(0); let file_id = match expander { Either::Left(expander) => { // 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_rules[0]))), + krate: CrateId(0), + ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_rules))), kind: MacroDefKind::BuiltIn(expander), local_inner: false, }; @@ -542,7 +545,7 @@ mod tests { krate, kind: MacroCallKind::FnLike(AstId::new( file_id.into(), - ast_id_map.ast_id(¯o_calls[0]), + ast_id_map.ast_id(¯o_call), )), }; @@ -552,13 +555,13 @@ mod tests { Either::Right(expander) => { // 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_rules[0]))), + krate, + ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_rules))), kind: MacroDefKind::BuiltInEager(expander), local_inner: false, }; - let args = macro_calls[0].token_tree().unwrap(); + let args = macro_call.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/db.rs b/crates/hir_expand/src/db.rs index 11b5b98c87d..4477d867f7c 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs @@ -129,7 +129,10 @@ fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc { fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option> { match id.kind { MacroDefKind::Declarative => { - let macro_call = id.ast_id?.to_node(db); + let macro_call = match id.ast_id?.to_node(db) { + syntax::ast::Macro::MacroRules(mac) => mac, + syntax::ast::Macro::MacroDef(_) => return None, + }; let arg = macro_call.token_tree()?; let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { log::warn!("fail on macro_def to token tree: {:#?}", arg); diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs index 5d3fa0518f4..7ab0a5e52eb 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs @@ -29,8 +29,8 @@ impl Hygiene { MacroCallId::LazyMacro(id) => { let loc = db.lookup_intern_macro(id); match loc.def.kind { - MacroDefKind::Declarative => (loc.def.krate, loc.def.local_inner), - MacroDefKind::BuiltIn(_) => (loc.def.krate, false), + MacroDefKind::Declarative => (Some(loc.def.krate), loc.def.local_inner), + MacroDefKind::BuiltIn(_) => (Some(loc.def.krate), false), MacroDefKind::BuiltInDerive(_) => (None, false), MacroDefKind::BuiltInEager(_) => (None, false), MacroDefKind::ProcMacro(_) => (None, false), diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index ae3086a95bd..d486186e59d 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs @@ -145,7 +145,10 @@ impl HirFileId { let arg_tt = loc.kind.arg(db)?; let def = loc.def.ast_id.and_then(|id| { - let def_tt = id.to_node(db).token_tree()?; + let def_tt = match id.to_node(db) { + ast::Macro::MacroRules(mac) => mac.token_tree()?, + ast::Macro::MacroDef(_) => return None, + }; Some(InFile::new(id.file_id, def_tt)) }); @@ -221,14 +224,8 @@ impl From for MacroCallId { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct MacroDefId { - // FIXME: krate and ast_id are currently optional because we don't have a - // definition location for built-in derives. There is one, though: the - // standard library defines them. The problem is that it uses the new - // `macro` syntax for this, which we don't support yet. As soon as we do - // (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 krate: CrateId, + 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 de97ec3c206..a7656b86486 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs @@ -686,6 +686,8 @@ mod clone { trait Clone { fn clone(&self) -> Self; } + #[rustc_builtin_macro] + macro Clone {} } "#, ); @@ -702,6 +704,8 @@ mod clone { trait Clone { fn clone(&self) -> Self; } + #[rustc_builtin_macro] + macro Clone {} } #[derive(Clone)] pub struct S; @@ -737,6 +741,8 @@ mod clone { trait Clone { fn clone(&self) -> Self; } + #[rustc_builtin_macro] + macro Clone {} } "#, ); diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 529004878d3..68c628d3143 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs @@ -221,6 +221,8 @@ struct Foo<|>; mod marker { trait Copy {} } +#[rustc_builtin_macro] +macro Copy {} "#, ); } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 0569cf1e5a7..3530a5fdb4b 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html @@ -38,6 +38,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
use inner::{self as inner_mod};
 mod inner {}
 
+#[rustc_builtin_macro]
+macro Copy {}
+
 // Needed for function consuming vs normal
 pub mod marker {
     #[lang = "copy"]
@@ -119,7 +122,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     f()
 }
 
-fn foobar() -> impl Copy {}
+fn foobar() -> impl Copy {}
 
 fn foo() {
     let bar = foobar();
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 1dc018a167b..f53d2c3ba41 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -12,6 +12,9 @@ fn test_highlighting() {
 use inner::{self as inner_mod};
 mod inner {}
 
+#[rustc_builtin_macro]
+macro Copy {}
+
 // Needed for function consuming vs normal
 pub mod marker {
     #[lang = "copy"]
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index 7844f9ed635..70c568ea1e7 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -19,8 +19,8 @@ pub use self::{
     expr_ext::{ArrayExprKind, BinOp, Effect, ElseBranch, LiteralKind, PrefixOp, RangeOp},
     generated::{nodes::*, tokens::*},
     node_ext::{
-        AttrKind, FieldKind, NameOrNameRef, PathSegmentKind, SelfParamKind, SlicePatComponents,
-        StructKind, TypeBoundKind, VisibilityKind,
+        AttrKind, FieldKind, Macro, NameOrNameRef, PathSegmentKind, SelfParamKind,
+        SlicePatComponents, StructKind, TypeBoundKind, VisibilityKind,
     },
     token_ext::*,
     traits::*,
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 0ad75214fc4..6eae323f4a2 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -286,6 +286,18 @@ impl MacroRules {
     pub fn token_tree(&self) -> Option { support::child(&self.syntax) }
 }
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct MacroDef {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ast::AttrsOwner for MacroDef {}
+impl ast::NameOwner for MacroDef {}
+impl ast::VisibilityOwner for MacroDef {}
+impl MacroDef {
+    pub fn macro_token(&self) -> Option { support::token(&self.syntax, T![macro]) }
+    pub fn args(&self) -> Option { support::child(&self.syntax) }
+    pub fn body(&self) -> Option { support::child(&self.syntax) }
+}
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Module {
     pub(crate) syntax: SyntaxNode,
 }
@@ -1332,6 +1344,7 @@ pub enum Item {
     Impl(Impl),
     MacroCall(MacroCall),
     MacroRules(MacroRules),
+    MacroDef(MacroDef),
     Module(Module),
     Static(Static),
     Struct(Struct),
@@ -1689,6 +1702,17 @@ impl AstNode for MacroRules {
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl AstNode for MacroDef {
+    fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_DEF }
+    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 {
@@ -3086,6 +3110,9 @@ impl From for Item {
 impl From for Item {
     fn from(node: MacroRules) -> Item { Item::MacroRules(node) }
 }
+impl From for Item {
+    fn from(node: MacroDef) -> Item { Item::MacroDef(node) }
+}
 impl From for Item {
     fn from(node: Module) -> Item { Item::Module(node) }
 }
@@ -3111,7 +3138,7 @@ impl AstNode for Item {
     fn can_cast(kind: SyntaxKind) -> bool {
         match kind {
             CONST | ENUM | EXTERN_BLOCK | EXTERN_CRATE | FN | IMPL | MACRO_CALL | MACRO_RULES
-            | MODULE | STATIC | STRUCT | TRAIT | TYPE_ALIAS | UNION | USE => true,
+            | MACRO_DEF | MODULE | STATIC | STRUCT | TRAIT | TYPE_ALIAS | UNION | USE => true,
             _ => false,
         }
     }
@@ -3125,6 +3152,7 @@ impl AstNode for Item {
             IMPL => Item::Impl(Impl { syntax }),
             MACRO_CALL => Item::MacroCall(MacroCall { syntax }),
             MACRO_RULES => Item::MacroRules(MacroRules { syntax }),
+            MACRO_DEF => Item::MacroDef(MacroDef { syntax }),
             MODULE => Item::Module(Module { syntax }),
             STATIC => Item::Static(Static { syntax }),
             STRUCT => Item::Struct(Struct { syntax }),
@@ -3146,6 +3174,7 @@ impl AstNode for Item {
             Item::Impl(it) => &it.syntax,
             Item::MacroCall(it) => &it.syntax,
             Item::MacroRules(it) => &it.syntax,
+            Item::MacroDef(it) => &it.syntax,
             Item::Module(it) => &it.syntax,
             Item::Static(it) => &it.syntax,
             Item::Struct(it) => &it.syntax,
@@ -3615,6 +3644,11 @@ impl std::fmt::Display for MacroRules {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
+impl std::fmt::Display for MacroDef {
+    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 c59a29eab30..40dec3c7f99 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -3,6 +3,7 @@
 
 use std::fmt;
 
+use ast::AttrsOwner;
 use itertools::Itertools;
 use parser::SyntaxKind;
 
@@ -31,6 +32,57 @@ fn text_of_first_token(node: &SyntaxNode) -> &SmolStr {
     node.green().children().next().and_then(|it| it.into_token()).unwrap().text()
 }
 
+pub enum Macro {
+    MacroRules(ast::MacroRules),
+    MacroDef(ast::MacroDef),
+}
+
+impl From for Macro {
+    fn from(it: ast::MacroRules) -> Self {
+        Macro::MacroRules(it)
+    }
+}
+
+impl From for Macro {
+    fn from(it: ast::MacroDef) -> Self {
+        Macro::MacroDef(it)
+    }
+}
+
+impl AstNode for Macro {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        match kind {
+            SyntaxKind::MACRO_RULES | SyntaxKind::MACRO_DEF => true,
+            _ => false,
+        }
+    }
+    fn cast(syntax: SyntaxNode) -> Option {
+        let res = match syntax.kind() {
+            SyntaxKind::MACRO_RULES => Macro::MacroRules(ast::MacroRules { syntax }),
+            SyntaxKind::MACRO_DEF => Macro::MacroDef(ast::MacroDef { syntax }),
+            _ => return None,
+        };
+        Some(res)
+    }
+    fn syntax(&self) -> &SyntaxNode {
+        match self {
+            Macro::MacroRules(it) => it.syntax(),
+            Macro::MacroDef(it) => it.syntax(),
+        }
+    }
+}
+
+impl NameOwner for Macro {
+    fn name(&self) -> Option {
+        match self {
+            Macro::MacroRules(mac) => mac.name(),
+            Macro::MacroDef(mac) => mac.name(),
+        }
+    }
+}
+
+impl AttrsOwner for Macro {}
+
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum AttrKind {
     Inner,
@@ -462,4 +514,6 @@ impl ast::DocCommentsOwner for ast::Const {}
 impl ast::DocCommentsOwner for ast::TypeAlias {}
 impl ast::DocCommentsOwner for ast::Impl {}
 impl ast::DocCommentsOwner for ast::MacroRules {}
+impl ast::DocCommentsOwner for ast::MacroDef {}
+impl ast::DocCommentsOwner for ast::Macro {}
 impl ast::DocCommentsOwner for ast::Use {}
diff --git a/crates/syntax/src/display.rs b/crates/syntax/src/display.rs
index d33bde30c59..391647fc671 100644
--- a/crates/syntax/src/display.rs
+++ b/crates/syntax/src/display.rs
@@ -76,8 +76,20 @@ pub fn type_label(node: &ast::TypeAlias) -> String {
     label.trim().to_owned()
 }
 
-pub fn macro_label(node: &ast::MacroRules) -> String {
+pub fn macro_label(node: &ast::Macro) -> 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)
+    match node {
+        ast::Macro::MacroRules(node) => {
+            let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" };
+            format!("{}macro_rules! {}", vis, name)
+        }
+        ast::Macro::MacroDef(node) => {
+            let mut s = String::new();
+            if let Some(vis) = node.visibility() {
+                format_to!(s, "{} ", vis);
+            }
+            format_to!(s, "macro {}", name);
+            s
+        }
+    }
 }