Repository: rust-analyzer/rust-analyzer
Pull request #10789 (2021-11-17 18:46:44)

internal: Check for derive attributes by item path, not `derive` identifier
Prior we only checked if an attribute is the exact `derive` identifier and nothing else to collect derive attributes. That means when we encounter the following:
```rs
#[::core::macros::builtin::derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ModPath {
    pub kind: PathKind,
    segments: Vec<Name>,
}
```
We won't actually expand the derive attributes, but instead we just expand the `derive` attribute with our dummy identity expander.

The changes here make it so we actually lookup the attribute path, check if it is the derive attribute and then collect the derives.



Patch:
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 4f68c91cc9..fd45eccf5a 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -8,7 +8,7 @@ use either::Either;
 use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
 use itertools::Itertools;
 use la_arena::ArenaMap;
-use mbe::{syntax_node_to_token_tree, DelimiterKind};
+use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
 use smallvec::{smallvec, SmallVec};
 use syntax::{
     ast::{self, AstNode, HasAttrs, IsString},
@@ -722,41 +722,35 @@ impl Attr {
     /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths
     /// to derive macros.
     ///
-    /// Returns `None` when the attribute is not a well-formed `#[derive]` attribute.
+    /// Returns `None` when the attribute does not have a well-formed `#[derive]` attribute input.
     pub(crate) fn parse_derive(&self) -> Option<impl Iterator<Item = ModPath>> {
-        if self.path.as_ident() != Some(&hir_expand::name![derive]) {
-            return None;
-        }
-
-        match self.input.as_deref() {
-            Some(AttrInput::TokenTree(args, _)) => {
-                let mut counter = 0;
-                let paths = args
-                    .token_trees
-                    .iter()
-                    .group_by(move |tt| {
-                        match tt {
-                            tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
-                                counter += 1;
-                            }
-                            _ => {}
-                        }
-                        counter
-                    })
-                    .into_iter()
-                    .map(|(_, tts)| {
-                        let segments = tts.filter_map(|tt| match tt {
-                            tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
-                            _ => None,
-                        });
-                        ModPath::from_segments(PathKind::Plain, segments)
-                    })
-                    .collect::<Vec<_>>();
-
-                Some(paths.into_iter())
+        if let Some(AttrInput::TokenTree(args, _)) = self.input.as_deref() {
+            if args.delimiter_kind() != Some(DelimiterKind::Parenthesis) {
+                return None;
             }
-            _ => None,
+            let mut counter = 0;
+            let paths = args
+                .token_trees
+                .iter()
+                .group_by(move |tt| {
+                    if let tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })) = tt {
+                        counter += 1;
+                    }
+                    counter
+                })
+                .into_iter()
+                .map(|(_, tts)| {
+                    let segments = tts.filter_map(|tt| match tt {
+                        tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
+                        _ => None,
+                    });
+                    ModPath::from_segments(PathKind::Plain, segments)
+                })
+                .collect::<Vec<_>>();
+
+            return Some(paths.into_iter());
         }
+        None
     }
 
     pub fn string_value(&self) -> Option<&SmolStr> {
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 1abda2d66a..b68ede4003 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -776,13 +776,10 @@ fn attr_macro_as_call_id(
     macro_attr: &Attr,
     db: &dyn db::DefDatabase,
     krate: CrateId,
-    resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
+    def: Option<MacroDefId>,
 ) -> Result<MacroCallId, UnresolvedMacro> {
     let attr_path = &item_attr.path;
-
-    let def = resolver(attr_path.clone())
-        .filter(MacroDefId::is_attribute)
-        .ok_or_else(|| UnresolvedMacro { path: attr_path.clone() })?;
+    let def = def.ok_or_else(|| UnresolvedMacro { path: attr_path.clone() })?;
     let last_segment =
         attr_path.segments().last().ok_or_else(|| UnresolvedMacro { path: attr_path.clone() })?;
     let mut arg = match macro_attr.input.as_deref() {
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index e45711db75..bafba0672f 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -9,7 +9,7 @@ use base_db::{CrateId, Edition, FileId, ProcMacroId};
 use cfg::{CfgExpr, CfgOptions};
 use hir_expand::{
     ast_id_map::FileAstId,
-    builtin_attr_macro::{find_builtin_attr, is_builtin_test_or_bench_attr},
+    builtin_attr_macro::find_builtin_attr,
     builtin_derive_macro::find_builtin_derive,
     builtin_fn_macro::find_builtin_macro,
     name::{name, AsName, Name},
@@ -781,7 +781,7 @@ impl DefCollector<'_> {
     }
 
     fn resolve_extern_crate(&self, name: &Name) -> PerNs {
-        if name == &name!(self) {
+        if *name == name!(self) {
             cov_mark::hit!(extern_crate_self_as);
             let root = match self.def_map.block {
                 Some(_) => {
@@ -1054,14 +1054,15 @@ impl DefCollector<'_> {
 
             match &directive.kind {
                 MacroDirectiveKind::FnLike { ast_id, expand_to } => {
-                    match macro_call_as_call_id(
+                    let call_id = macro_call_as_call_id(
                         ast_id,
                         *expand_to,
                         self.db,
                         self.def_map.krate,
                         &resolver,
                         &mut |_err| (),
-                    ) {
+                    );
+                    match call_id {
                         Ok(Ok(call_id)) => {
                             resolved.push((directive.module_id, call_id, directive.depth));
                             res = ReachedFixedPoint::No;
@@ -1071,13 +1072,14 @@ impl DefCollector<'_> {
                     }
                 }
                 MacroDirectiveKind::Derive { ast_id, derive_attr } => {
-                    match derive_macro_as_call_id(
+                    let call_id = derive_macro_as_call_id(
                         ast_id,
                         *derive_attr,
                         self.db,
                         self.def_map.krate,
                         &resolver,
-                    ) {
+                    );
+                    match call_id {
                         Ok(call_id) => {
                             self.def_map.modules[directive.module_id].scope.add_derive_macro_invoc(
                                 ast_id.ast_id,
@@ -1089,73 +1091,103 @@ impl DefCollector<'_> {
                             res = ReachedFixedPoint::No;
                             return false;
                         }
-                        Err(UnresolvedMacro { .. }) => (),
+                        Err(UnresolvedMacro { .. }) => {}
                     }
                 }
                 MacroDirectiveKind::Attr { ast_id, mod_item, attr } => {
+                    let file_id = ast_id.ast_id.file_id;
+                    let mut recollect_without = |collector: &mut Self, item_tree| {
+                        // Remove the original directive since we resolved it.
+                        let mod_dir = collector.mod_dirs[&directive.module_id].clone();
+                        collector.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id);
+                        ModCollector {
+                            def_collector: collector,
+                            macro_depth: directive.depth,
+                            module_id: directive.module_id,
+                            tree_id: TreeId::new(file_id, None),
+                            item_tree,
+                            mod_dir,
+                        }
+                        .collect(&[*mod_item]);
+                        res = ReachedFixedPoint::No;
+                        false
+                    };
+
                     if let Some(ident) = ast_id.path.as_ident() {
                         if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id.ast_id) {
                             if helpers.contains(ident) {
                                 cov_mark::hit!(resolved_derive_helper);
-
                                 // Resolved to derive helper. Collect the item's attributes again,
                                 // starting after the derive helper.
-                                let file_id = ast_id.ast_id.file_id;
                                 let item_tree = self.db.file_item_tree(file_id);
-                                let mod_dir = self.mod_dirs[&directive.module_id].clone();
-                                self.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id);
-                                ModCollector {
-                                    def_collector: &mut *self,
-                                    macro_depth: directive.depth,
-                                    module_id: directive.module_id,
-                                    tree_id: TreeId::new(file_id, None),
-                                    item_tree: &item_tree,
-                                    mod_dir,
-                                }
-                                .collect(&[*mod_item]);
+                                return recollect_without(self, &item_tree);
+                            }
+                        }
+                    }
 
-                                // Remove the original directive since we resolved it.
+                    let def = resolver(ast_id.path.clone()).filter(MacroDefId::is_attribute);
+                    if matches!(
+                        def,
+                        Some(MacroDefId {  kind:MacroDefKind::BuiltInAttr(expander, _),.. })
+                        if expander.is_derive()
+                    ) {
+                        // Resolved to `#[derive]`
+                        let file_id = ast_id.ast_id.file_id;
+                        let item_tree = self.db.file_item_tree(file_id);
+
+                        let ast_id: FileAstId<ast::Item> = match *mod_item {
+                            ModItem::Struct(it) => item_tree[it].ast_id.upcast(),
+                            ModItem::Union(it) => item_tree[it].ast_id.upcast(),
+                            ModItem::Enum(it) => item_tree[it].ast_id.upcast(),
+                            _ => {
+                                // Cannot use derive on this item.
+                                // FIXME: diagnose
                                 res = ReachedFixedPoint::No;
                                 return false;
                             }
+                        };
+
+                        match attr.parse_derive() {
+                            Some(derive_macros) => {
+                                for path in derive_macros {
+                                    let ast_id = AstIdWithPath::new(file_id, ast_id, path);
+                                    self.unresolved_macros.push(MacroDirective {
+                                        module_id: directive.module_id,
+                                        depth: directive.depth + 1,
+                                        kind: MacroDirectiveKind::Derive {
+                                            ast_id,
+                                            derive_attr: attr.id,
+                                        },
+                                    });
+                                }
+                            }
+                            None => {
+                                // FIXME: diagnose
+                                tracing::debug!("malformed derive: {:?}", attr);
+                            }
                         }
+
+                        return recollect_without(self, &item_tree);
                     }
 
                     if !self.db.enable_proc_attr_macros() {
                         return true;
                     }
 
-                    // Not resolved to a derive helper, so try to resolve as a macro.
-                    match attr_macro_as_call_id(
-                        ast_id,
-                        attr,
-                        self.db,
-                        self.def_map.krate,
-                        &resolver,
-                    ) {
+                    // Not resolved to a derive helper or the derive attribute, so try to resolve as a normal attribute.
+                    match attr_macro_as_call_id(ast_id, attr, self.db, self.def_map.krate, def) {
                         Ok(call_id) => {
                             let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
 
                             // Skip #[test]/#[bench] expansion, which would merely result in more memory usage
                             // due to duplicating functions into macro expansions
-                            if is_builtin_test_or_bench_attr(loc.def) {
-                                let file_id = ast_id.ast_id.file_id;
+                            if matches!(
+                                loc.def.kind,
+                                MacroDefKind::BuiltInAttr(expander, _)
+                                if expander.is_test() || expander.is_bench()
+                            ) {
                                 let item_tree = self.db.file_item_tree(file_id);
-                                let mod_dir = self.mod_dirs[&directive.module_id].clone();
-                                self.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id);
-                                ModCollector {
-                                    def_collector: &mut *self,
-                                    macro_depth: directive.depth,
-                                    module_id: directive.module_id,
-                                    tree_id: TreeId::new(file_id, None),
-                                    item_tree: &item_tree,
-                                    mod_dir,
-                                }
-                                .collect(&[*mod_item]);
-
-                                // Remove the original directive since we resolved it.
-                                res = ReachedFixedPoint::No;
-                                return false;
+                                return recollect_without(self, &item_tree);
                             }
 
                             if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind {
@@ -1171,21 +1203,7 @@ impl DefCollector<'_> {
 
                                     let file_id = ast_id.ast_id.file_id;
                                     let item_tree = self.db.file_item_tree(file_id);
-                                    let mod_dir = self.mod_dirs[&directive.module_id].clone();
-                                    self.skip_attrs
-                                        .insert(InFile::new(file_id, *mod_item), attr.id);
-                                    ModCollector {
-                                        def_collector: &mut *self,
-                                        macro_depth: directive.depth,
-                                        module_id: directive.module_id,
-                                        tree_id: TreeId::new(file_id, None),
-                                        item_tree: &item_tree,
-                                        mod_dir,
-                                    }
-                                    .collect(&[*mod_item]);
-
-                                    // Remove the macro directive.
-                                    return false;
+                                    return recollect_without(self, &item_tree);
                                 }
                             }
 
@@ -1281,7 +1299,7 @@ impl DefCollector<'_> {
         for directive in &self.unresolved_macros {
             match &directive.kind {
                 MacroDirectiveKind::FnLike { ast_id, expand_to } => {
-                    match macro_call_as_call_id(
+                    let macro_call_as_call_id = macro_call_as_call_id(
                         ast_id,
                         *expand_to,
                         self.db,
@@ -1297,15 +1315,13 @@ impl DefCollector<'_> {
                             resolved_res.resolved_def.take_macros()
                         },
                         &mut |_| (),
-                    ) {
-                        Ok(_) => (),
-                        Err(UnresolvedMacro { path }) => {
-                            self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
-                                directive.module_id,
-                                ast_id.ast_id,
-                                path,
-                            ));
-                        }
+                    );
+                    if let Err(UnresolvedMacro { path }) = macro_call_as_call_id {
+                        self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
+                            directive.module_id,
+                            ast_id.ast_id,
+                            path,
+                        ));
                     }
                 }
                 MacroDirectiveKind::Derive { .. } | MacroDirectiveKind::Attr { .. } => {
@@ -1747,26 +1763,23 @@ impl ModCollector<'_, '_> {
             });
 
         for attr in iter {
-            if attr.path.as_ident() == Some(&hir_expand::name![derive]) {
-                self.collect_derive(attr, mod_item);
-            } else if self.is_builtin_or_registered_attr(&attr.path) {
+            if self.is_builtin_or_registered_attr(&attr.path) {
                 continue;
-            } else {
-                tracing::debug!("non-builtin attribute {}", attr.path);
+            }
+            tracing::debug!("non-builtin attribute {}", attr.path);
 
-                let ast_id = AstIdWithPath::new(
-                    self.file_id(),
-                    mod_item.ast_id(self.item_tree),
-                    attr.path.as_ref().clone(),
-                );
-                self.def_collector.unresolved_macros.push(MacroDirective {
-                    module_id: self.module_id,
-                    depth: self.macro_depth + 1,
-                    kind: MacroDirectiveKind::Attr { ast_id, attr: attr.clone(), mod_item },
-                });
+            let ast_id = AstIdWithPath::new(
+                self.file_id(),
+                mod_item.ast_id(self.item_tree),
+                attr.path.as_ref().clone(),
+            );
+            self.def_collector.unresolved_macros.push(MacroDirective {
+                module_id: self.module_id,
+                depth: self.macro_depth + 1,
+                kind: MacroDirectiveKind::Attr { ast_id, attr: attr.clone(), mod_item },
+            });
 
-                return Err(());
-            }
+            return Err(());
         }
 
         Ok(())
@@ -1800,36 +1813,6 @@ impl ModCollector<'_, '_> {
         false
     }
 
-    fn collect_derive(&mut self, attr: &Attr, mod_item: ModItem) {
-        let ast_id: FileAstId<ast::Item> = match mod_item {
-            ModItem::Struct(it) => self.item_tree[it].ast_id.upcast(),
-            ModItem::Union(it) => self.item_tree[it].ast_id.upcast(),
-            ModItem::Enum(it) => self.item_tree[it].ast_id.upcast(),
-            _ => {
-                // Cannot use derive on this item.
-                // FIXME: diagnose
-                return;
-            }
-        };
-
-        match attr.parse_derive() {
-            Some(derive_macros) => {
-                for path in derive_macros {
-                    let ast_id = AstIdWithPath::new(self.file_id(), ast_id, path);
-                    self.def_collector.unresolved_macros.push(MacroDirective {
-                        module_id: self.module_id,
-                        depth: self.macro_depth + 1,
-                        kind: MacroDirectiveKind::Derive { ast_id, derive_attr: attr.id },
-                    });
-                }
-            }
-            None => {
-                // FIXME: diagnose
-                tracing::debug!("malformed derive: {:?}", attr);
-            }
-        }
-    }
-
     /// If `attrs` registers a procedural macro, collects its definition.
     fn collect_proc_macro_def(&mut self, func_name: &Name, ast_id: AstId<ast::Fn>, attrs: &Attrs) {
         // FIXME: this should only be done in the root module of `proc-macro` crates, not everywhere
diff --git a/crates/hir_expand/src/builtin_attr_macro.rs b/crates/hir_expand/src/builtin_attr_macro.rs
index ec587daf9b..8da8c2ee4f 100644
--- a/crates/hir_expand/src/builtin_attr_macro.rs
+++ b/crates/hir_expand/src/builtin_attr_macro.rs
@@ -36,6 +36,18 @@ macro_rules! register_builtin {
     };
 }
 
+impl BuiltinAttrExpander {
+    pub fn is_derive(self) -> bool {
+        matches!(self, BuiltinAttrExpander::Derive)
+    }
+    pub fn is_test(self) -> bool {
+        matches!(self, BuiltinAttrExpander::Test)
+    }
+    pub fn is_bench(self) -> bool {
+        matches!(self, BuiltinAttrExpander::Bench)
+    }
+}
+
 register_builtin! {
     (bench, Bench) => dummy_attr_expand,
     (cfg_accessible, CfgAccessible) => dummy_attr_expand,
@@ -46,16 +58,6 @@ register_builtin! {
     (test_case, TestCase) => dummy_attr_expand
 }
 
-pub fn is_builtin_test_or_bench_attr(makro: MacroDefId) -> bool {
-    match makro.kind {
-        MacroDefKind::BuiltInAttr(expander, ..) => {
-            BuiltinAttrExpander::find_by_name(&name!(test)) == Some(expander)
-                || BuiltinAttrExpander::find_by_name(&name!(bench)) == Some(expander)
-        }
-        _ => false,
-    }
-}
-
 pub fn find_builtin_attr(
     ident: &name::Name,
     krate: CrateId,
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index c389d895f2..57c078ef57 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -367,9 +367,7 @@ fn main() {
         check(
             r#"
 //- proc_macros: identity
-
-#[rustc_builtin_macro]
-pub macro Clone {}
+//- minicore: clone, derive
 
 #[proc_macros::identity]
 #[derive(C$0lone)]
@@ -377,7 +375,7 @@ struct Foo {}
 "#,
             expect![[r#"
                 Clone
-                impl< >crate::clone::Clone for Foo< >{}
+                impl< >core::clone::Clone for Foo< >{}
 
             "#]],
         );
@@ -387,10 +385,7 @@ struct Foo {}
     fn macro_expand_derive2() {
         check(
             r#"
-#[rustc_builtin_macro]
-pub macro Clone {}
-#[rustc_builtin_macro]
-pub macro Copy {}
+//- minicore: copy, clone, derive
 
 #[derive(Cop$0y)]
 #[derive(Clone)]
@@ -398,7 +393,7 @@ struct Foo {}
 "#,
             expect![[r#"
                 Copy
-                impl< >crate::marker::Copy for Foo< >{}
+                impl< >core::marker::Copy for Foo< >{}
 
             "#]],
         );
@@ -408,19 +403,16 @@ struct Foo {}
     fn macro_expand_derive_multi() {
         check(
             r#"
-#[rustc_builtin_macro]
-pub macro Clone {}
-#[rustc_builtin_macro]
-pub macro Copy {}
+//- minicore: copy, clone, derive
 
 #[derive(Cop$0y, Clone)]
 struct Foo {}
 "#,
             expect![[r#"
                 Copy, Clone
-                impl< >crate::marker::Copy for Foo< >{}
+                impl< >core::marker::Copy for Foo< >{}
 
-                impl< >crate::clone::Clone for Foo< >{}
+                impl< >core::clone::Clone for Foo< >{}
 
             "#]],
         );


Test patch:
diff --git a/crates/hir_def/src/macro_expansion_tests/builtin_derive_macro.rs b/crates/hir_def/src/macro_expansion_tests/builtin_derive_macro.rs
index aa50fa6d33..8ee33101e4 100644
--- a/crates/hir_def/src/macro_expansion_tests/builtin_derive_macro.rs
+++ b/crates/hir_def/src/macro_expansion_tests/builtin_derive_macro.rs
@@ -26,12 +26,16 @@ fn test_copy_expand_in_core() {
     check(
         r#"
 #[rustc_builtin_macro]
+macro derive {}
+#[rustc_builtin_macro]
 macro Copy {}
 #[derive(Copy)]
 struct Foo;
 "#,
         expect![[r##"
 #[rustc_builtin_macro]
+macro derive {}
+#[rustc_builtin_macro]
 macro Copy {}
 #[derive(Copy)]
 struct Foo;
diff --git a/crates/hir_def/src/macro_expansion_tests/proc_macros.rs b/crates/hir_def/src/macro_expansion_tests/proc_macros.rs
index 81f58fa2ce..901872edda 100644
--- a/crates/hir_def/src/macro_expansion_tests/proc_macros.rs
+++ b/crates/hir_def/src/macro_expansion_tests/proc_macros.rs
@@ -31,6 +31,7 @@ fn derive_censoring() {
     check(
         r#"
 //- proc_macros: derive_identity
+//- minicore:derive
 #[attr1]
 #[derive(Foo)]
 #[derive(proc_macros::DeriveIdentity)]
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 763dff5a21..fbb1320d6e 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -669,19 +669,20 @@ pub struct bar;
 fn expand_derive() {
     let map = compute_crate_def_map(
         r#"
-        //- /main.rs crate:main deps:core
-        use core::Copy;
-
-        #[derive(Copy, core::Clone)]
-        struct Foo;
+//- /main.rs crate:main deps:core
+use core::Copy;
 
-        //- /core.rs crate:core
-        #[rustc_builtin_macro]
-        pub macro Copy {}
+#[core::derive(Copy, core::Clone)]
+struct Foo;
 
-        #[rustc_builtin_macro]
-        pub macro Clone {}
-        "#,
+//- /core.rs crate:core
+#[rustc_builtin_macro]
+pub macro derive($item:item) {}
+#[rustc_builtin_macro]
+pub macro Copy {}
+#[rustc_builtin_macro]
+pub macro Clone {}
+"#,
     );
     assert_eq!(map.modules[map.root].scope.impls().len(), 2);
 }
@@ -712,17 +713,19 @@ fn builtin_derive_with_unresolved_attributes_fall_back() {
     cov_mark::check!(unresolved_attribute_fallback);
     let map = compute_crate_def_map(
         r#"
-        //- /main.rs crate:main deps:core
-        use core::Clone;
+//- /main.rs crate:main deps:core
+use core::{Clone, derive};
 
-        #[derive(Clone)]
-        #[unresolved]
-        struct Foo;
+#[derive(Clone)]
+#[unresolved]
+struct Foo;
 
-        //- /core.rs crate:core
-        #[rustc_builtin_macro]
-        pub macro Clone {}
-        "#,
+//- /core.rs crate:core
+#[rustc_builtin_macro]
+pub macro derive($item:item) {}
+#[rustc_builtin_macro]
+pub macro Clone {}
+"#,
     );
     assert_eq!(map.modules[map.root].scope.impls().len(), 1);
 }
@@ -799,6 +802,9 @@ fn resolves_derive_helper() {
     check(
         r#"
 //- /main.rs crate:main deps:proc
+#[rustc_builtin_macro]
+pub macro derive($item:item) {}
+
 #[derive(proc::Derive)]
 #[helper]
 #[unresolved]
@@ -811,6 +817,7 @@ fn derive() {}
         expect![[r#"
             crate
             S: t v
+            derive: m
         "#]],
     );
 }
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index ac5d749b09..a61175f273 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -974,61 +974,12 @@ fn infer_builtin_macros_env() {
 fn infer_derive_clone_simple() {
     check_types(
         r#"
-//- /main.rs crate:main deps:core
+//- minicore: derive, clone
 #[derive(Clone)]
 struct S;
 fn test() {
     S.clone();
 } //^^^^^^^^^ S
-
-//- /lib.rs crate:core
-pub mod prelude {
-    pub mod rust_2018 {
-        #[rustc_builtin_macro]
-        pub macro Clone {}
-        pub use crate::clone::Clone;
-    }
-}
-
-pub mod clone {
-    pub trait Clone {
-        fn clone(&self) -> Self;
-    }
-}
-"#,
-    );
-}
-
-#[test]
-fn infer_derive_clone_in_core() {
-    check_types(
-        r#"
-//- /lib.rs crate:core
-#[prelude_import]
-use prelude::rust_2018::*;
-
-pub mod prelude {
-    pub mod rust_2018 {
-        #[rustc_builtin_macro]
-        pub macro Clone {}
-        pub use crate::clone::Clone;
-    }
-}
-
-pub mod clone {
-    pub trait Clone {
-        fn clone(&self) -> Self;
-    }
-}
-
-#[derive(Clone)]
-pub struct S;
-
-//- /main.rs crate:main deps:core
-use core::S;
-fn test() {
-    S.clone();
-} //^^^^^^^^^ S
 "#,
     );
 }
@@ -1037,7 +988,7 @@ fn test() {
 fn infer_derive_clone_with_params() {
     check_types(
         r#"
-//- /main.rs crate:main deps:core
+//- minicore: clone, derive
 #[derive(Clone)]
 struct S;
 #[derive(Clone)]
@@ -1048,21 +999,6 @@ fn test() {
     x;
   //^ (Wrapper<S>, {unknown})
 }
-
-//- /lib.rs crate:core
-pub mod prelude {
-    pub mod rust_2018 {
-        #[rustc_builtin_macro]
-        pub macro Clone {}
-        pub use crate::clone::Clone;
-    }
-}
-
-pub mod clone {
-    pub trait Clone {
-        fn clone(&self) -> Self;
-    }
-}
 "#,
     );
 }
@@ -1072,7 +1008,7 @@ fn infer_custom_derive_simple() {
     // FIXME: this test current now do nothing
     check_types(
         r#"
-//- /main.rs crate:main
+//- minicore: derive
 use foo::Foo;
 
 #[derive(Foo)]
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs
index e5963bae74..9e555dc49e 100644
--- a/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -805,6 +805,9 @@ bar = {path = "../bar"}
 
 //- /foo/src/main.rs
 use bar::Bar;
+
+#[rustc_builtin_macro]
+macro derive($item:item) {}
 trait Bar {
   fn bar();
 }
@@ -875,7 +878,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
     let res = server.send_request::<HoverRequest>(HoverParams {
         text_document_position_params: TextDocumentPositionParams::new(
             server.doc_id("foo/src/main.rs"),
-            Position::new(7, 9),
+            Position::new(10, 9),
         ),
         work_done_progress_params: Default::default(),
     });