aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-12-05 20:00:20 +0000
committerGitHub <[email protected]>2019-12-05 20:00:20 +0000
commit6e10a9f57815ad865a570816436adfdf0de1cdf0 (patch)
tree410c416bfe9daa05743ce8c49418c20df28dd625 /crates/ra_hir_def
parent217a6fa4a387dbfe6ac725b6dba2f15d6532679f (diff)
parent10697041c1c72ddbe27c41912e691656be6ccce4 (diff)
Merge #2479
2479: Add expansion infrastructure for derive macros r=matklad a=flodiebold I thought I'd experiment a bit with attribute macro/derive expansion, and here's what I've got so far. It has dummy implementations of the Copy / Clone derives, to show that the approach works; it doesn't add any attribute macro support, but I think that fits into the architecture. Basically, during raw item collection, we look at the attributes and generate macro calls for them if necessary. Currently I only do this for derives, and just add the derive macro calls as separate calls next to the item. I think for derives, it's important that they don't obscure the actual item, since they can't actually change it (e.g. sending the item token tree through macro expansion unnecessarily might make completion within it more complicated). Attribute macros would have to be recognized at that stage and replace the item (i.e., the raw item collector will just emit an attribute macro call, and not the item). I think when we implement this, we should try to recognize known inert attributes, so that we don't do macro expansion unnecessarily; anything that isn't known needs to be treated as a possible attribute macro call (since the raw item collector can't resolve the macro yet). There's basically no name resolution for attribute macros implemented, I just hardcoded the built-in derives. In the future, the built-ins should work within the normal name resolution infrastructure; the problem there is that the builtin stubs in `std` use macros 2.0, which we don't support yet (and adding support is outside the scope of this). One aspect that I don't really have a solution for, but I don't know how important it is, is removing the attribute itself from its input. I'm pretty sure rustc leaves out the attribute macro from the input, but to do that, we'd have to create a completely new syntax node. I guess we could do it when / after converting to a token tree. Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir_def')
-rw-r--r--crates/ra_hir_def/src/attr.rs4
-rw-r--r--crates/ra_hir_def/src/body.rs6
-rw-r--r--crates/ra_hir_def/src/docs.rs2
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs73
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs15
-rw-r--r--crates/ra_hir_def/src/nameres/tests/macros.rs24
-rw-r--r--crates/ra_hir_def/src/path.rs5
7 files changed, 118 insertions, 11 deletions
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
index 7f9a6e7ca..2f8f02d82 100644
--- a/crates/ra_hir_def/src/attr.rs
+++ b/crates/ra_hir_def/src/attr.rs
@@ -61,7 +61,9 @@ impl Attrs {
61 AdtId::UnionId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db), 61 AdtId::UnionId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
62 }, 62 },
63 AttrDefId::TraitId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db), 63 AttrDefId::TraitId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
64 AttrDefId::MacroDefId(it) => attrs_from_ast(it.ast_id, db), 64 AttrDefId::MacroDefId(it) => {
65 it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db))
66 }
65 AttrDefId::ImplId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db), 67 AttrDefId::ImplId(it) => attrs_from_ast(it.lookup_intern(db).ast_id, db),
66 AttrDefId::ConstId(it) => attrs_from_loc(it.lookup(db), db), 68 AttrDefId::ConstId(it) => attrs_from_loc(it.lookup(db), db),
67 AttrDefId::StaticId(it) => attrs_from_loc(it.lookup(db), db), 69 AttrDefId::StaticId(it) => attrs_from_loc(it.lookup(db), db),
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index ef1816836..7b385f3fd 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -6,7 +6,9 @@ pub mod scope;
6use std::{ops::Index, sync::Arc}; 6use std::{ops::Index, sync::Arc};
7 7
8use either::Either; 8use either::Either;
9use hir_expand::{hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId, MacroFileKind}; 9use hir_expand::{
10 hygiene::Hygiene, AstId, HirFileId, InFile, MacroCallKind, MacroDefId, MacroFileKind,
11};
10use ra_arena::{map::ArenaMap, Arena}; 12use ra_arena::{map::ArenaMap, Arena};
11use ra_syntax::{ast, AstNode, AstPtr}; 13use ra_syntax::{ast, AstNode, AstPtr};
12use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
@@ -46,7 +48,7 @@ impl Expander {
46 48
47 if let Some(path) = macro_call.path().and_then(|path| self.parse_path(path)) { 49 if let Some(path) = macro_call.path().and_then(|path| self.parse_path(path)) {
48 if let Some(def) = self.resolve_path_as_macro(db, &path) { 50 if let Some(def) = self.resolve_path_as_macro(db, &path) {
49 let call_id = def.as_call_id(db, ast_id); 51 let call_id = def.as_call_id(db, MacroCallKind::FnLike(ast_id));
50 let file_id = call_id.as_file(MacroFileKind::Expr); 52 let file_id = call_id.as_file(MacroFileKind::Expr);
51 if let Some(node) = db.parse_or_expand(file_id) { 53 if let Some(node) = db.parse_or_expand(file_id) {
52 if let Some(expr) = ast::Expr::cast(node) { 54 if let Some(expr) = ast::Expr::cast(node) {
diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs
index 3fc6d6934..61727bd26 100644
--- a/crates/ra_hir_def/src/docs.rs
+++ b/crates/ra_hir_def/src/docs.rs
@@ -60,7 +60,7 @@ impl Documentation {
60 docs_from_ast(&src.value[it.local_id]) 60 docs_from_ast(&src.value[it.local_id])
61 } 61 }
62 AttrDefId::TraitId(it) => docs_from_ast(&it.source(db).value), 62 AttrDefId::TraitId(it) => docs_from_ast(&it.source(db).value),
63 AttrDefId::MacroDefId(it) => docs_from_ast(&it.ast_id.to_node(db)), 63 AttrDefId::MacroDefId(it) => docs_from_ast(&it.ast_id?.to_node(db)),
64 AttrDefId::ConstId(it) => docs_from_ast(&it.lookup(db).source(db).value), 64 AttrDefId::ConstId(it) => docs_from_ast(&it.lookup(db).source(db).value),
65 AttrDefId::StaticId(it) => docs_from_ast(&it.lookup(db).source(db).value), 65 AttrDefId::StaticId(it) => docs_from_ast(&it.lookup(db).source(db).value),
66 AttrDefId::FunctionId(it) => docs_from_ast(&it.lookup(db).source(db).value), 66 AttrDefId::FunctionId(it) => docs_from_ast(&it.lookup(db).source(db).value),
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index 9d948d4f4..08693cb13 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -4,9 +4,10 @@
4//! resolves imports and expands macros. 4//! resolves imports and expands macros.
5 5
6use hir_expand::{ 6use hir_expand::{
7 builtin_derive::find_builtin_derive,
7 builtin_macro::find_builtin_macro, 8 builtin_macro::find_builtin_macro,
8 name::{self, AsName, Name}, 9 name::{self, AsName, Name},
9 HirFileId, MacroCallId, MacroDefId, MacroDefKind, MacroFileKind, 10 HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, MacroFileKind,
10}; 11};
11use ra_cfg::CfgOptions; 12use ra_cfg::CfgOptions;
12use ra_db::{CrateId, FileId}; 13use ra_db::{CrateId, FileId};
@@ -58,6 +59,7 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C
58 glob_imports: FxHashMap::default(), 59 glob_imports: FxHashMap::default(),
59 unresolved_imports: Vec::new(), 60 unresolved_imports: Vec::new(),
60 unexpanded_macros: Vec::new(), 61 unexpanded_macros: Vec::new(),
62 unexpanded_attribute_macros: Vec::new(),
61 mod_dirs: FxHashMap::default(), 63 mod_dirs: FxHashMap::default(),
62 macro_stack_monitor: MacroStackMonitor::default(), 64 macro_stack_monitor: MacroStackMonitor::default(),
63 poison_macros: FxHashSet::default(), 65 poison_macros: FxHashSet::default(),
@@ -102,6 +104,7 @@ struct DefCollector<'a, DB> {
102 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, LocalImportId)>>, 104 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, LocalImportId)>>,
103 unresolved_imports: Vec<(LocalModuleId, LocalImportId, raw::ImportData)>, 105 unresolved_imports: Vec<(LocalModuleId, LocalImportId, raw::ImportData)>,
104 unexpanded_macros: Vec<(LocalModuleId, AstId<ast::MacroCall>, Path)>, 106 unexpanded_macros: Vec<(LocalModuleId, AstId<ast::MacroCall>, Path)>,
107 unexpanded_attribute_macros: Vec<(LocalModuleId, AstId<ast::ModuleItem>, Path)>,
105 mod_dirs: FxHashMap<LocalModuleId, ModDir>, 108 mod_dirs: FxHashMap<LocalModuleId, ModDir>,
106 109
107 /// Some macro use `$tt:tt which mean we have to handle the macro perfectly 110 /// Some macro use `$tt:tt which mean we have to handle the macro perfectly
@@ -470,6 +473,8 @@ where
470 473
471 fn resolve_macros(&mut self) -> ReachedFixedPoint { 474 fn resolve_macros(&mut self) -> ReachedFixedPoint {
472 let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); 475 let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
476 let mut attribute_macros =
477 std::mem::replace(&mut self.unexpanded_attribute_macros, Vec::new());
473 let mut resolved = Vec::new(); 478 let mut resolved = Vec::new();
474 let mut res = ReachedFixedPoint::Yes; 479 let mut res = ReachedFixedPoint::Yes;
475 macros.retain(|(module_id, ast_id, path)| { 480 macros.retain(|(module_id, ast_id, path)| {
@@ -482,7 +487,19 @@ where
482 ); 487 );
483 488
484 if let Some(def) = resolved_res.resolved_def.take_macros() { 489 if let Some(def) = resolved_res.resolved_def.take_macros() {
485 let call_id = def.as_call_id(self.db, *ast_id); 490 let call_id = def.as_call_id(self.db, MacroCallKind::FnLike(*ast_id));
491 resolved.push((*module_id, call_id, def));
492 res = ReachedFixedPoint::No;
493 return false;
494 }
495
496 true
497 });
498 attribute_macros.retain(|(module_id, ast_id, path)| {
499 let resolved_res = self.resolve_attribute_macro(path);
500
501 if let Some(def) = resolved_res {
502 let call_id = def.as_call_id(self.db, MacroCallKind::Attr(*ast_id));
486 resolved.push((*module_id, call_id, def)); 503 resolved.push((*module_id, call_id, def));
487 res = ReachedFixedPoint::No; 504 res = ReachedFixedPoint::No;
488 return false; 505 return false;
@@ -492,6 +509,7 @@ where
492 }); 509 });
493 510
494 self.unexpanded_macros = macros; 511 self.unexpanded_macros = macros;
512 self.unexpanded_attribute_macros = attribute_macros;
495 513
496 for (module_id, macro_call_id, macro_def_id) in resolved { 514 for (module_id, macro_call_id, macro_def_id) in resolved {
497 self.collect_macro_expansion(module_id, macro_call_id, macro_def_id); 515 self.collect_macro_expansion(module_id, macro_call_id, macro_def_id);
@@ -500,6 +518,20 @@ where
500 res 518 res
501 } 519 }
502 520
521 fn resolve_attribute_macro(&self, path: &Path) -> Option<MacroDefId> {
522 // FIXME this is currently super hacky, just enough to support the
523 // built-in derives
524 if let Some(name) = path.as_ident() {
525 // FIXME this should actually be handled with the normal name
526 // resolution; the std lib defines built-in stubs for the derives,
527 // but these are new-style `macro`s, which we don't support yet
528 if let Some(def_id) = find_builtin_derive(name) {
529 return Some(def_id);
530 }
531 }
532 None
533 }
534
503 fn collect_macro_expansion( 535 fn collect_macro_expansion(
504 &mut self, 536 &mut self,
505 module_id: LocalModuleId, 537 module_id: LocalModuleId,
@@ -587,7 +619,9 @@ where
587 .def_collector 619 .def_collector
588 .unresolved_imports 620 .unresolved_imports
589 .push((self.module_id, import_id, self.raw_items[import_id].clone())), 621 .push((self.module_id, import_id, self.raw_items[import_id].clone())),
590 raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]), 622 raw::RawItemKind::Def(def) => {
623 self.define_def(&self.raw_items[def], &item.attrs)
624 }
591 raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]), 625 raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
592 raw::RawItemKind::Impl(imp) => { 626 raw::RawItemKind::Impl(imp) => {
593 let module = ModuleId { 627 let module = ModuleId {
@@ -682,10 +716,16 @@ where
682 res 716 res
683 } 717 }
684 718
685 fn define_def(&mut self, def: &raw::DefData) { 719 fn define_def(&mut self, def: &raw::DefData, attrs: &Attrs) {
686 let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; 720 let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
687 let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id); 721 let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id);
688 722
723 // FIXME: check attrs to see if this is an attribute macro invocation;
724 // in which case we don't add the invocation, just a single attribute
725 // macro invocation
726
727 self.collect_derives(attrs, def);
728
689 let name = def.name.clone(); 729 let name = def.name.clone();
690 let def: PerNs = match def.kind { 730 let def: PerNs = match def.kind {
691 raw::DefKind::Function(ast_id) => { 731 raw::DefKind::Function(ast_id) => {
@@ -736,6 +776,23 @@ where
736 self.def_collector.update(self.module_id, None, &[(name, resolution)]) 776 self.def_collector.update(self.module_id, None, &[(name, resolution)])
737 } 777 }
738 778
779 fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) {
780 for derive_subtree in attrs.by_key("derive").tt_values() {
781 // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree
782 for tt in &derive_subtree.token_trees {
783 let ident = match &tt {
784 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident,
785 tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => continue, // , is ok
786 _ => continue, // anything else would be an error (which we currently ignore)
787 };
788 let path = Path::from_tt_ident(ident);
789
790 let ast_id = AstId::new(self.file_id, def.kind.ast_id());
791 self.def_collector.unexpanded_attribute_macros.push((self.module_id, ast_id, path));
792 }
793 }
794 }
795
739 fn collect_macro(&mut self, mac: &raw::MacroData) { 796 fn collect_macro(&mut self, mac: &raw::MacroData) {
740 let ast_id = AstId::new(self.file_id, mac.ast_id); 797 let ast_id = AstId::new(self.file_id, mac.ast_id);
741 798
@@ -759,8 +816,8 @@ where
759 if is_macro_rules(&mac.path) { 816 if is_macro_rules(&mac.path) {
760 if let Some(name) = &mac.name { 817 if let Some(name) = &mac.name {
761 let macro_id = MacroDefId { 818 let macro_id = MacroDefId {
762 ast_id, 819 ast_id: Some(ast_id),
763 krate: self.def_collector.def_map.krate, 820 krate: Some(self.def_collector.def_map.krate),
764 kind: MacroDefKind::Declarative, 821 kind: MacroDefKind::Declarative,
765 }; 822 };
766 self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export); 823 self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
@@ -773,7 +830,8 @@ where
773 if let Some(macro_def) = mac.path.as_ident().and_then(|name| { 830 if let Some(macro_def) = mac.path.as_ident().and_then(|name| {
774 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) 831 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
775 }) { 832 }) {
776 let macro_call_id = macro_def.as_call_id(self.def_collector.db, ast_id); 833 let macro_call_id =
834 macro_def.as_call_id(self.def_collector.db, MacroCallKind::FnLike(ast_id));
777 835
778 self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_def); 836 self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_def);
779 return; 837 return;
@@ -829,6 +887,7 @@ mod tests {
829 glob_imports: FxHashMap::default(), 887 glob_imports: FxHashMap::default(),
830 unresolved_imports: Vec::new(), 888 unresolved_imports: Vec::new(),
831 unexpanded_macros: Vec::new(), 889 unexpanded_macros: Vec::new(),
890 unexpanded_attribute_macros: Vec::new(),
832 mod_dirs: FxHashMap::default(), 891 mod_dirs: FxHashMap::default(),
833 macro_stack_monitor: monitor, 892 macro_stack_monitor: monitor,
834 poison_macros: FxHashSet::default(), 893 poison_macros: FxHashSet::default(),
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
index de4e706c2..a2821e1c3 100644
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -184,6 +184,21 @@ pub(super) enum DefKind {
184 TypeAlias(FileAstId<ast::TypeAliasDef>), 184 TypeAlias(FileAstId<ast::TypeAliasDef>),
185} 185}
186 186
187impl DefKind {
188 pub fn ast_id(&self) -> FileAstId<ast::ModuleItem> {
189 match self {
190 DefKind::Function(it) => it.upcast(),
191 DefKind::Struct(it) => it.upcast(),
192 DefKind::Union(it) => it.upcast(),
193 DefKind::Enum(it) => it.upcast(),
194 DefKind::Const(it) => it.upcast(),
195 DefKind::Static(it) => it.upcast(),
196 DefKind::Trait(it) => it.upcast(),
197 DefKind::TypeAlias(it) => it.upcast(),
198 }
199 }
200}
201
187#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 202#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
188pub(super) struct Macro(RawId); 203pub(super) struct Macro(RawId);
189impl_arena_id!(Macro); 204impl_arena_id!(Macro);
diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs
index 704065633..cfa4ecb1a 100644
--- a/crates/ra_hir_def/src/nameres/tests/macros.rs
+++ b/crates/ra_hir_def/src/nameres/tests/macros.rs
@@ -600,3 +600,27 @@ fn macro_dollar_crate_is_correct_in_indirect_deps() {
600 ⋮bar: t v 600 ⋮bar: t v
601 "###); 601 "###);
602} 602}
603
604#[test]
605fn expand_derive() {
606 let map = compute_crate_def_map(
607 "
608 //- /main.rs
609 #[derive(Clone)]
610 struct Foo;
611 ",
612 );
613 assert_eq!(map.modules[map.root].impls.len(), 1);
614}
615
616#[test]
617fn expand_multiple_derive() {
618 let map = compute_crate_def_map(
619 "
620 //- /main.rs
621 #[derive(Copy, Clone)]
622 struct Foo;
623 ",
624 );
625 assert_eq!(map.modules[map.root].impls.len(), 2);
626}
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index 3030dcdf6..e547b2f03 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -199,6 +199,11 @@ impl Path {
199 name_ref.as_name().into() 199 name_ref.as_name().into()
200 } 200 }
201 201
202 /// Converts an `tt::Ident` into a single-identifier `Path`.
203 pub(crate) fn from_tt_ident(ident: &tt::Ident) -> Path {
204 ident.as_name().into()
205 }
206
202 /// `true` is this path is a single identifier, like `foo` 207 /// `true` is this path is a single identifier, like `foo`
203 pub fn is_ident(&self) -> bool { 208 pub fn is_ident(&self) -> bool {
204 self.kind == PathKind::Plain && self.segments.len() == 1 209 self.kind == PathKind::Plain && self.segments.len() == 1