about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2021-08-24 16:33:52 +0200
committerLukas Wirth <lukastw97@gmail.com>2021-08-24 16:33:52 +0200
commitd99b81f839ec2b7e4a0eb37c96b40e6c5b050b72 (patch)
treedf61391bfd3aa535b401f2fb0efcd123ad86e8ed
parent6287d388c0d9a1d89f333cad23b8fbb87607d0f6 (diff)
downloadrust-d99b81f839ec2b7e4a0eb37c96b40e6c5b050b72.tar.gz
rust-d99b81f839ec2b7e4a0eb37c96b40e6c5b050b72.zip
Expand derive macros under cursor in `Expand Macro Recursively`
-rw-r--r--crates/hir/src/semantics.rs16
-rw-r--r--crates/hir/src/semantics/source_to_def.rs9
-rw-r--r--crates/hir_def/src/child_by_source.rs7
-rw-r--r--crates/hir_def/src/item_scope.rs22
-rw-r--r--crates/hir_def/src/keys.rs1
-rw-r--r--crates/hir_def/src/nameres/collector.rs6
-rw-r--r--crates/ide/src/expand_macro.rs31
7 files changed, 90 insertions, 2 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 000daf76c25..b0435a4bacb 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -148,6 +148,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.expand_attr_macro(item)
     }
 
+    pub fn expand_derive_macro(&self, derive: &ast::Attr) -> Option<SyntaxNode> {
+        self.imp.expand_derive_macro(derive)
+    }
+
     pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
         self.imp.is_attr_macro_call(item)
     }
@@ -385,6 +389,18 @@ impl<'db> SemanticsImpl<'db> {
         Some(node)
     }
 
+    fn expand_derive_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
+        let item = attr.syntax().parent().and_then(ast::Item::cast)?;
+        let sa = self.analyze(item.syntax());
+        let item = InFile::new(sa.file_id, &item);
+        let src = InFile::new(sa.file_id, attr.clone());
+        let macro_call_id = self.with_ctx(|ctx| ctx.attr_to_derive_macro_call(item, src))?;
+        let file_id = macro_call_id.as_file();
+        let node = self.db.parse_or_expand(file_id)?;
+        self.cache(node.clone(), file_id);
+        Some(node)
+    }
+
     fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
         let sa = self.analyze(item.syntax());
         let src = InFile::new(sa.file_id, item.clone());
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 773eab79315..acf545816bd 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -242,6 +242,15 @@ impl SourceToDefCtx<'_, '_> {
         map[keys::ATTR_MACRO].get(&src).copied()
     }
 
+    pub(super) fn attr_to_derive_macro_call(
+        &mut self,
+        item: InFile<&ast::Item>,
+        src: InFile<ast::Attr>,
+    ) -> Option<MacroCallId> {
+        let map = self.dyn_map(item)?;
+        map[keys::DERIVE_MACRO].get(&src).copied()
+    }
+
     fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
         &mut self,
         src: InFile<Ast>,
diff --git a/crates/hir_def/src/child_by_source.rs b/crates/hir_def/src/child_by_source.rs
index 1a2b47cd030..feb6a88060a 100644
--- a/crates/hir_def/src/child_by_source.rs
+++ b/crates/hir_def/src/child_by_source.rs
@@ -6,6 +6,7 @@
 
 use either::Either;
 use hir_expand::HirFileId;
+use syntax::ast::AttrsOwner;
 
 use crate::{
     db::DefDatabase,
@@ -108,6 +109,12 @@ impl ChildBySource for ItemScope {
             let item = ast_id.with_value(ast_id.to_node(db.upcast()));
             res[keys::ATTR_MACRO].insert(item, call_id);
         });
+        self.derive_macro_invocs().for_each(|(ast_id, (attr_id, call_id))| {
+            let item = ast_id.to_node(db.upcast());
+            if let Some(attr) = item.attrs().nth(attr_id.ast_index as usize) {
+                res[keys::DERIVE_MACRO].insert(ast_id.with_value(attr), call_id);
+            }
+        });
 
         fn add_module_def(
             db: &dyn DefDatabase,
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index 3825911f1dc..51586142e22 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -12,8 +12,8 @@ use stdx::format_to;
 use syntax::ast;
 
 use crate::{
-    db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, ImplId,
-    LocalModuleId, MacroDefId, ModuleDefId, ModuleId, TraitId,
+    attr::AttrId, db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType,
+    ConstId, ImplId, LocalModuleId, MacroDefId, ModuleDefId, ModuleId, TraitId,
 };
 
 #[derive(Copy, Clone)]
@@ -61,6 +61,7 @@ pub struct ItemScope {
     // be all resolved to the last one defined if shadowing happens.
     legacy_macros: FxHashMap<Name, MacroDefId>,
     attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>,
+    derive_macros: FxHashMap<AstId<ast::Item>, (AttrId, MacroCallId)>,
 }
 
 pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
@@ -182,6 +183,21 @@ impl ItemScope {
         self.attr_macros.iter().map(|(k, v)| (*k, *v))
     }
 
+    pub(crate) fn add_derive_macro_invoc(
+        &mut self,
+        item: AstId<ast::Item>,
+        call: MacroCallId,
+        attr_id: AttrId,
+    ) {
+        self.derive_macros.insert(item, (attr_id, call));
+    }
+
+    pub(crate) fn derive_macro_invocs(
+        &self,
+    ) -> impl Iterator<Item = (AstId<ast::Item>, (AttrId, MacroCallId))> + '_ {
+        self.derive_macros.iter().map(|(k, v)| (*k, *v))
+    }
+
     pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
         self.unnamed_trait_imports.get(&tr).copied()
     }
@@ -320,6 +336,7 @@ impl ItemScope {
             unnamed_trait_imports,
             legacy_macros,
             attr_macros,
+            derive_macros,
         } = self;
         types.shrink_to_fit();
         values.shrink_to_fit();
@@ -331,6 +348,7 @@ impl ItemScope {
         unnamed_trait_imports.shrink_to_fit();
         legacy_macros.shrink_to_fit();
         attr_macros.shrink_to_fit();
+        derive_macros.shrink_to_fit();
     }
 }
 
diff --git a/crates/hir_def/src/keys.rs b/crates/hir_def/src/keys.rs
index 688cd9fcff6..ef015935002 100644
--- a/crates/hir_def/src/keys.rs
+++ b/crates/hir_def/src/keys.rs
@@ -33,6 +33,7 @@ pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new();
 
 pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();
 pub const ATTR_MACRO: Key<ast::Item, MacroCallId> = Key::new();
+pub const DERIVE_MACRO: Key<ast::Attr, MacroCallId> = Key::new();
 
 /// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
 /// equal if they point to exactly the same object.
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index f8b3c3949f6..e9c33392f3c 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -1047,6 +1047,12 @@ impl DefCollector<'_> {
                         &resolver,
                     ) {
                         Ok(call_id) => {
+                            self.def_map.modules[directive.module_id].scope.add_derive_macro_invoc(
+                                ast_id.ast_id,
+                                call_id,
+                                *derive_attr,
+                            );
+
                             resolved.push((directive.module_id, call_id, directive.depth));
                             res = ReachedFixedPoint::No;
                             return false;
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index 814d28e7b6c..363b77967c3 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -2,6 +2,7 @@ use std::iter;
 
 use hir::Semantics;
 use ide_db::{helpers::pick_best_token, RootDatabase};
+use itertools::Itertools;
 use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, WalkEvent, T};
 
 use crate::FilePosition;
@@ -33,6 +34,18 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
     let mut expanded = None;
     let mut name = None;
     for node in tok.ancestors() {
+        if let Some(attr) = ast::Attr::cast(node.clone()) {
+            if let Some((path, tt)) = attr.as_simple_call() {
+                if path == "derive" {
+                    let mut tt = tt.syntax().children_with_tokens().skip(1).join("");
+                    tt.pop();
+                    name = Some(tt);
+                    expanded = sema.expand_derive_macro(&attr);
+                    break;
+                }
+            }
+        }
+
         if let Some(item) = ast::Item::cast(node.clone()) {
             if let Some(def) = sema.resolve_attr_macro_call(&item) {
                 name = def.name(db).map(|name| name.to_string());
@@ -325,4 +338,22 @@ fn main() {
                 0 "#]],
         );
     }
+
+    #[test]
+    fn macro_expand_derive() {
+        check(
+            r#"
+
+#[rustc_builtin_macro]
+pub macro Clone {}
+
+#[derive(C$0lone)]
+struct Foo {}
+"#,
+            expect![[r#"
+                Clone
+                impl< >crate::clone::Clone for Foo< >{}
+            "#]],
+        );
+    }
 }