Repository: rust-analyzer/rust-analyzer
Pull request #6897 (2020-12-15 19:39:25)

Basic support for macros 2.0
This adds support for (built-in-only) macros 2.0, and removes some hacks used for builtin derives, which are declared via macros 2.0 in libcore.

First steps for https://github.com/rust-analyzer/rust-analyzer/issues/2248.

Blocked on https://github.com/rust-analyzer/ungrammar/pull/16.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index ff2c33f45d..e44b7d1d92 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 42dc35b762..9bfcd215a5 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<Module> {
-        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 04845037fe..ecf3194c6a 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<ast::MacroRules> {
+    type Ast = ast::Macro;
+    fn source(self, db: &dyn HirDatabase) -> InFile<ast::Macro> {
         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()),
@@ -129,7 +129,7 @@ impl HasSource for TypeParam {
     type Ast = Either<ast::Trait, ast::TypeParam>;
     fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> {
         let child_source = self.id.parent.child_source(db.upcast());
-        child_source.map(|it| it[self.id.local_id].clone())
+        child_source.map(|it| it.type_params[self.id.local_id].clone())
     }
 }
 
@@ -137,6 +137,6 @@ impl HasSource for LifetimeParam {
     type Ast = ast::LifetimeParam;
     fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> {
         let child_source = self.id.parent.child_source(db.upcast());
-        child_source.map(|it| it[self.id.local_id].clone())
+        child_source.map(|it| it.lifetime_params[self.id.local_id].clone())
     }
 }
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index a333d7aea9..3efca5baa5 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<ChildContainer> {
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs
index eafa3abb6a..6539959c3c 100644
--- a/crates/hir_def/src/adt.rs
+++ b/crates/hir_def/src/adt.rs
@@ -145,12 +145,10 @@ impl EnumData {
     }
 }
 
-impl HasChildSource<LocalEnumVariantId> for EnumId {
+impl HasChildSource for EnumId {
+    type ChildId = LocalEnumVariantId;
     type Value = ast::Variant;
-    fn child_source(
-        &self,
-        db: &dyn DefDatabase,
-    ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> {
+    fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
         let src = self.lookup(db).source(db);
         let mut trace = Trace::new_for_map();
         lower_enum(db, &mut trace, &src, self.lookup(db).container.module(db));
@@ -214,10 +212,11 @@ impl VariantData {
     }
 }
 
-impl HasChildSource<LocalFieldId> for VariantId {
+impl HasChildSource for VariantId {
+    type ChildId = LocalFieldId;
     type Value = Either<ast::TupleField, ast::RecordField>;
 
-    fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
+    fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
         let (src, module_id) = match self {
             VariantId::EnumVariantId(it) => {
                 // I don't really like the fact that we call into parent source
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index bdba4c33ea..23e2fd7641 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/generics.rs b/crates/hir_def/src/generics.rs
index 924046435b..81912a4546 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -19,7 +19,7 @@ use crate::{
     db::DefDatabase,
     dyn_map::DynMap,
     keys,
-    src::{HasChildSource, HasSource},
+    src::HasSource,
     type_ref::{LifetimeRef, TypeBound, TypeRef},
     AdtId, GenericDefId, LifetimeParamId, LocalLifetimeParamId, LocalTypeParamId, Lookup,
     TypeParamId,
@@ -73,9 +73,9 @@ pub enum WherePredicateTypeTarget {
 }
 
 #[derive(Default)]
-pub(crate) struct SourceMap {
-    pub(crate) type_params: ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>,
-    lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>,
+pub struct SourceMaps {
+    pub type_params: ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>,
+    pub lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>,
 }
 
 impl GenericParams {
@@ -133,9 +133,9 @@ impl GenericParams {
         Arc::new(generics)
     }
 
-    fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) {
+    fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMaps>) {
         let mut generics = GenericParams::default();
-        let mut sm = SourceMap::default();
+        let mut sm = SourceMaps::default();
 
         // FIXME: add `: Sized` bound for everything except for `Self` in traits
         let file_id = match def {
@@ -214,7 +214,7 @@ impl GenericParams {
     pub(crate) fn fill(
         &mut self,
         lower_ctx: &LowerCtx,
-        sm: &mut SourceMap,
+        sm: &mut SourceMaps,
         node: &dyn GenericParamsOwner,
     ) {
         if let Some(params) = node.generic_param_list() {
@@ -241,7 +241,7 @@ impl GenericParams {
     fn fill_params(
         &mut self,
         lower_ctx: &LowerCtx,
-        sm: &mut SourceMap,
+        sm: &mut SourceMaps,
         params: ast::GenericParamList,
     ) {
         for type_param in params.type_params() {
@@ -345,24 +345,10 @@ impl GenericParams {
         })
     }
 }
-
-impl HasChildSource<LocalTypeParamId> for GenericDefId {
-    type Value = Either<ast::Trait, ast::TypeParam>;
-    fn child_source(
-        &self,
-        db: &dyn DefDatabase,
-    ) -> InFile<ArenaMap<LocalTypeParamId, Self::Value>> {
-        GenericParams::new(db, *self).1.map(|source_maps| source_maps.type_params)
-    }
-}
-
-impl HasChildSource<LocalLifetimeParamId> for GenericDefId {
-    type Value = ast::LifetimeParam;
-    fn child_source(
-        &self,
-        db: &dyn DefDatabase,
-    ) -> InFile<ArenaMap<LocalLifetimeParamId, Self::Value>> {
-        GenericParams::new(db, *self).1.map(|source_maps| source_maps.lifetime_params)
+impl GenericDefId {
+    // FIXME: Change HasChildSource's ChildId AssocItem to be a generic parameter instead
+    pub fn child_source(&self, db: &dyn DefDatabase) -> InFile<SourceMaps> {
+        GenericParams::new(db, *self).1
     }
 }
 
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index a8b3fe844a..62ab3b2bd6 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 1c9babf371..8cd0b18ccd 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<Mod>,
     macro_calls: Arena<MacroCall>,
     macro_rules: Arena<MacroRules>,
+    macro_defs: Arena<MacroDef>,
     exprs: Arena<Expr>,
 
     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<ast::MacroRules>,
 }
 
+/// "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<ast::MacroDef>,
+}
+
 // 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 b39d7fb7ac..1dc06a2115 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::<SmallVec<_>>()))
             }
@@ -561,6 +563,18 @@ impl Ctx {
         Some(id(self.data().macro_rules.alloc(res)))
     }
 
+    fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option<FileItemTreeId<MacroDef>> {
+        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<ModItem> {
         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 85cc342c44..785895277f 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<MacroDefId> {
-        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/src.rs b/crates/hir_def/src/src.rs
index f67244b464..7a79b03149 100644
--- a/crates/hir_def/src/src.rs
+++ b/crates/hir_def/src/src.rs
@@ -36,7 +36,8 @@ impl<N: ItemTreeNode> HasSource for ItemLoc<N> {
     }
 }
 
-pub trait HasChildSource<ChildId> {
+pub trait HasChildSource {
+    type ChildId;
     type Value;
-    fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<ChildId, Self::Value>>;
+    fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>>;
 }
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs
index 988a60d56a..ad378762a1 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)
             }
-        }
-
-        pub fn find_builtin_derive(ident: &name::Name) -> Option<MacroDefId> {
-            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 })
+            fn find_by_name(name: &name::Name) -> Option<Self> {
+                match name {
+                    $( id if id == &name::name![$trait] => Some(BuiltinDeriveExpander::$trait), )*
+                     _ => None,
+                }
+            }
         }
+
     };
 }
 
@@ -54,6 +53,20 @@ register_builtin! {
     PartialEq => partial_eq_expand
 }
 
+pub fn find_builtin_derive(
+    ident: &name::Name,
+    krate: CrateId,
+    ast_id: AstId<ast::Macro>,
+) -> Option<MacroDefId> {
+    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 bd92238257..dddbbcdac4 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::MacroRules>,
+    ast_id: AstId<ast::Macro>,
 ) -> Option<MacroDefId> {
     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(&macro_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(&macro_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(&macro_rules[0]))),
+                    krate: CrateId(0),
+                    ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_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(&macro_calls[0]),
+                        ast_id_map.ast_id(&macro_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(&macro_rules[0]))),
+                    krate,
+                    ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_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 11b5b98c87..4477d867f7 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<AstIdMap> {
 fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
     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 5d3fa0518f..7ab0a5e52e 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 ae3086a95b..d486186e59 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<EagerMacroId> 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<CrateId>,
-    pub ast_id: Option<AstId<ast::MacroRules>>,
+    pub krate: CrateId,
+    pub ast_id: Option<AstId<ast::Macro>>,
     pub kind: MacroDefKind,
 
     pub local_inner: bool,
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index 529004878d..68c628d314 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/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index 7844f9ed63..70c568ea1e 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 0ad75214fc..6eae323f4a 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<TokenTree> { 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<SyntaxToken> { support::token(&self.syntax, T![macro]) }
+    pub fn args(&self) -> Option<TokenTree> { support::child(&self.syntax) }
+    pub fn body(&self) -> Option<TokenTree> { 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<Self> {
+        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<Self> {
@@ -3086,6 +3110,9 @@ impl From<MacroCall> for Item {
 impl From<MacroRules> for Item {
     fn from(node: MacroRules) -> Item { Item::MacroRules(node) }
 }
+impl From<MacroDef> for Item {
+    fn from(node: MacroDef) -> Item { Item::MacroDef(node) }
+}
 impl From<Module> 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 c59a29eab3..40dec3c7f9 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<ast::MacroRules> for Macro {
+    fn from(it: ast::MacroRules) -> Self {
+        Macro::MacroRules(it)
+    }
+}
+
+impl From<ast::MacroDef> 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<Self> {
+        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<ast::Name> {
+        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 d33bde30c5..391647fc67 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
+        }
+    }
 }


Test patch:
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 305fca0f9c..6fe2ee78a1 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_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index de97ec3c20..a7656b8648 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/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 0569cf1e5a..3530a5fdb4 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
 <pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="punctuation">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="punctuation">}</span><span class="punctuation">;</span>
 <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="punctuation">{</span><span class="punctuation">}</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="punctuation">{</span><span class="punctuation">}</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="punctuation">{</span>
     <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"copy"</span><span class="attribute attribute">]</span>
@@ -119,7 +122,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="value_param callable">f</span><span class="punctuation">(</span><span class="punctuation">)</span>
 <span class="punctuation">}</span>
 
-<span class="keyword">fn</span> <span class="function declaration">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="keyword">impl</span> <span class="unresolved_reference">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+<span class="keyword">fn</span> <span class="function declaration">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="keyword">impl</span> <span class="macro">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span>
 
 <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
     <span class="keyword">let</span> <span class="variable declaration">bar</span> <span class="operator">=</span> <span class="function">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 1dc018a167..f53d2c3ba4 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"]