aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/code_model/src.rs5
-rw-r--r--crates/ra_hir/src/from_source.rs4
-rw-r--r--crates/ra_hir/src/source_binder.rs5
-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
-rw-r--r--crates/ra_hir_expand/src/ast_id_map.rs12
-rw-r--r--crates/ra_hir_expand/src/builtin_derive.rs301
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs37
-rw-r--r--crates/ra_hir_expand/src/db.rs19
-rw-r--r--crates/ra_hir_expand/src/hygiene.rs3
-rw-r--r--crates/ra_hir_expand/src/lib.rs76
-rw-r--r--crates/ra_hir_expand/src/name.rs17
-rw-r--r--crates/ra_hir_expand/src/quote.rs10
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs51
-rw-r--r--crates/ra_mbe/src/syntax_bridge.rs44
20 files changed, 632 insertions, 81 deletions
diff --git a/crates/ra_hir/src/code_model/src.rs b/crates/ra_hir/src/code_model/src.rs
index d9bccd902..78a454082 100644
--- a/crates/ra_hir/src/code_model/src.rs
+++ b/crates/ra_hir/src/code_model/src.rs
@@ -105,7 +105,10 @@ impl HasSource for TypeAlias {
105impl HasSource for MacroDef { 105impl HasSource for MacroDef {
106 type Ast = ast::MacroCall; 106 type Ast = ast::MacroCall;
107 fn source(self, db: &impl DefDatabase) -> InFile<ast::MacroCall> { 107 fn source(self, db: &impl DefDatabase) -> InFile<ast::MacroCall> {
108 InFile { file_id: self.id.ast_id.file_id, value: self.id.ast_id.to_node(db) } 108 InFile {
109 file_id: self.id.ast_id.expect("MacroDef without ast_id").file_id,
110 value: self.id.ast_id.expect("MacroDef without ast_id").to_node(db),
111 }
109 } 112 }
110} 113}
111impl HasSource for ImplBlock { 114impl HasSource for ImplBlock {
diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs
index 6fa947759..5cb222bd3 100644
--- a/crates/ra_hir/src/from_source.rs
+++ b/crates/ra_hir/src/from_source.rs
@@ -93,9 +93,9 @@ impl FromSource for MacroDef {
93 93
94 let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax())); 94 let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
95 let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?; 95 let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?;
96 let krate = module.krate().crate_id(); 96 let krate = Some(module.krate().crate_id());
97 97
98 let ast_id = AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value)); 98 let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value)));
99 99
100 let id: MacroDefId = MacroDefId { krate, ast_id, kind }; 100 let id: MacroDefId = MacroDefId { krate, ast_id, kind };
101 Some(MacroDef { id }) 101 Some(MacroDef { id })
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index db0451059..42c392513 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -20,7 +20,8 @@ use hir_def::{
20 AssocItemId, DefWithBodyId, 20 AssocItemId, DefWithBodyId,
21}; 21};
22use hir_expand::{ 22use hir_expand::{
23 hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroFileKind, 23 hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind,
24 MacroFileKind,
24}; 25};
25use ra_syntax::{ 26use ra_syntax::{
26 ast::{self, AstNode}, 27 ast::{self, AstNode},
@@ -456,7 +457,7 @@ impl SourceAnalyzer {
456 db.ast_id_map(macro_call.file_id).ast_id(macro_call.value), 457 db.ast_id_map(macro_call.file_id).ast_id(macro_call.value),
457 ); 458 );
458 Some(Expansion { 459 Some(Expansion {
459 macro_call_id: def.as_call_id(db, ast_id), 460 macro_call_id: def.as_call_id(db, MacroCallKind::FnLike(ast_id)),
460 macro_file_kind: to_macro_file_kind(macro_call.value), 461 macro_file_kind: to_macro_file_kind(macro_call.value),
461 }) 462 })
462 } 463 }
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
diff --git a/crates/ra_hir_expand/src/ast_id_map.rs b/crates/ra_hir_expand/src/ast_id_map.rs
index cb464c3ff..a764bdf24 100644
--- a/crates/ra_hir_expand/src/ast_id_map.rs
+++ b/crates/ra_hir_expand/src/ast_id_map.rs
@@ -39,6 +39,16 @@ impl<N: AstNode> Hash for FileAstId<N> {
39 } 39 }
40} 40}
41 41
42impl<N: AstNode> FileAstId<N> {
43 // Can't make this a From implementation because of coherence
44 pub fn upcast<M: AstNode>(self) -> FileAstId<M>
45 where
46 M: From<N>,
47 {
48 FileAstId { raw: self.raw, _ty: PhantomData }
49 }
50}
51
42#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 52#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
43struct ErasedFileAstId(RawId); 53struct ErasedFileAstId(RawId);
44impl_arena_id!(ErasedFileAstId); 54impl_arena_id!(ErasedFileAstId);
@@ -53,7 +63,7 @@ impl AstIdMap {
53 pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap { 63 pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap {
54 assert!(node.parent().is_none()); 64 assert!(node.parent().is_none());
55 let mut res = AstIdMap { arena: Arena::default() }; 65 let mut res = AstIdMap { arena: Arena::default() };
56 // By walking the tree in bread-first order we make sure that parents 66 // By walking the tree in breadth-first order we make sure that parents
57 // get lower ids then children. That is, adding a new child does not 67 // get lower ids then children. That is, adding a new child does not
58 // change parent's id. This means that, say, adding a new function to a 68 // change parent's id. This means that, say, adding a new function to a
59 // trait does not change ids of top-level items, which helps caching. 69 // trait does not change ids of top-level items, which helps caching.
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs
new file mode 100644
index 000000000..78fa9b09a
--- /dev/null
+++ b/crates/ra_hir_expand/src/builtin_derive.rs
@@ -0,0 +1,301 @@
1//! Builtin derives.
2
3use log::debug;
4
5use ra_parser::FragmentKind;
6use ra_syntax::{
7 ast::{self, AstNode, ModuleItemOwner, NameOwner, TypeParamsOwner},
8 match_ast,
9};
10
11use crate::db::AstDatabase;
12use crate::{name, quote, MacroCallId, MacroDefId, MacroDefKind};
13
14macro_rules! register_builtin {
15 ( $(($name:ident, $kind: ident) => $expand:ident),* ) => {
16 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17 pub enum BuiltinDeriveExpander {
18 $($kind),*
19 }
20
21 impl BuiltinDeriveExpander {
22 pub fn expand(
23 &self,
24 db: &dyn AstDatabase,
25 id: MacroCallId,
26 tt: &tt::Subtree,
27 ) -> Result<tt::Subtree, mbe::ExpandError> {
28 let expander = match *self {
29 $( BuiltinDeriveExpander::$kind => $expand, )*
30 };
31 expander(db, id, tt)
32 }
33 }
34
35 pub fn find_builtin_derive(ident: &name::Name) -> Option<MacroDefId> {
36 let kind = match ident {
37 $( id if id == &name::$name => BuiltinDeriveExpander::$kind, )*
38 _ => return None,
39 };
40
41 Some(MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(kind) })
42 }
43 };
44}
45
46register_builtin! {
47 (COPY_TRAIT, Copy) => copy_expand,
48 (CLONE_TRAIT, Clone) => clone_expand,
49 (DEFAULT_TRAIT, Default) => default_expand,
50 (DEBUG_TRAIT, Debug) => debug_expand,
51 (HASH_TRAIT, Hash) => hash_expand,
52 (ORD_TRAIT, Ord) => ord_expand,
53 (PARTIAL_ORD_TRAIT, PartialOrd) => partial_ord_expand,
54 (EQ_TRAIT, Eq) => eq_expand,
55 (PARTIAL_EQ_TRAIT, PartialEq) => partial_eq_expand
56}
57
58struct BasicAdtInfo {
59 name: tt::Ident,
60 type_params: usize,
61}
62
63fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> {
64 let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, FragmentKind::Items)?; // FragmentKind::Items doesn't parse attrs?
65 let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
66 debug!("derive node didn't parse");
67 mbe::ExpandError::UnexpectedToken
68 })?;
69 let item = macro_items.items().next().ok_or_else(|| {
70 debug!("no module item parsed");
71 mbe::ExpandError::NoMatchingRule
72 })?;
73 let node = item.syntax();
74 let (name, params) = match_ast! {
75 match node {
76 ast::StructDef(it) => { (it.name(), it.type_param_list()) },
77 ast::EnumDef(it) => { (it.name(), it.type_param_list()) },
78 ast::UnionDef(it) => { (it.name(), it.type_param_list()) },
79 _ => {
80 debug!("unexpected node is {:?}", node);
81 return Err(mbe::ExpandError::ConversionError)
82 },
83 }
84 };
85 let name = name.ok_or_else(|| {
86 debug!("parsed item has no name");
87 mbe::ExpandError::NoMatchingRule
88 })?;
89 let name_token_id = token_map.token_by_range(name.syntax().text_range()).ok_or_else(|| {
90 debug!("name token not found");
91 mbe::ExpandError::ConversionError
92 })?;
93 let name_token = tt::Ident { id: name_token_id, text: name.text().clone() };
94 let type_params = params.map_or(0, |type_param_list| type_param_list.type_params().count());
95 Ok(BasicAdtInfo { name: name_token, type_params })
96}
97
98fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> {
99 let mut result = Vec::<tt::TokenTree>::new();
100 result.push(tt::Leaf::Punct(tt::Punct { char: '<', spacing: tt::Spacing::Alone }).into());
101 for i in 0..n {
102 if i > 0 {
103 result
104 .push(tt::Leaf::Punct(tt::Punct { char: ',', spacing: tt::Spacing::Alone }).into());
105 }
106 result.push(
107 tt::Leaf::Ident(tt::Ident {
108 id: tt::TokenId::unspecified(),
109 text: format!("T{}", i).into(),
110 })
111 .into(),
112 );
113 result.extend(bound.iter().cloned());
114 }
115 result.push(tt::Leaf::Punct(tt::Punct { char: '>', spacing: tt::Spacing::Alone }).into());
116 result
117}
118
119fn expand_simple_derive(
120 tt: &tt::Subtree,
121 trait_path: tt::Subtree,
122) -> Result<tt::Subtree, mbe::ExpandError> {
123 let info = parse_adt(tt)?;
124 let name = info.name;
125 let trait_path_clone = trait_path.token_trees.clone();
126 let bound = (quote! { : ##trait_path_clone }).token_trees;
127 let type_params = make_type_args(info.type_params, bound);
128 let type_args = make_type_args(info.type_params, Vec::new());
129 let trait_path = trait_path.token_trees;
130 let expanded = quote! {
131 impl ##type_params ##trait_path for #name ##type_args {}
132 };
133 Ok(expanded)
134}
135
136fn copy_expand(
137 _db: &dyn AstDatabase,
138 _id: MacroCallId,
139 tt: &tt::Subtree,
140) -> Result<tt::Subtree, mbe::ExpandError> {
141 expand_simple_derive(tt, quote! { std::marker::Copy })
142}
143
144fn clone_expand(
145 _db: &dyn AstDatabase,
146 _id: MacroCallId,
147 tt: &tt::Subtree,
148) -> Result<tt::Subtree, mbe::ExpandError> {
149 expand_simple_derive(tt, quote! { std::clone::Clone })
150}
151
152fn default_expand(
153 _db: &dyn AstDatabase,
154 _id: MacroCallId,
155 tt: &tt::Subtree,
156) -> Result<tt::Subtree, mbe::ExpandError> {
157 expand_simple_derive(tt, quote! { std::default::Default })
158}
159
160fn debug_expand(
161 _db: &dyn AstDatabase,
162 _id: MacroCallId,
163 tt: &tt::Subtree,
164) -> Result<tt::Subtree, mbe::ExpandError> {
165 expand_simple_derive(tt, quote! { std::fmt::Debug })
166}
167
168fn hash_expand(
169 _db: &dyn AstDatabase,
170 _id: MacroCallId,
171 tt: &tt::Subtree,
172) -> Result<tt::Subtree, mbe::ExpandError> {
173 expand_simple_derive(tt, quote! { std::hash::Hash })
174}
175
176fn eq_expand(
177 _db: &dyn AstDatabase,
178 _id: MacroCallId,
179 tt: &tt::Subtree,
180) -> Result<tt::Subtree, mbe::ExpandError> {
181 expand_simple_derive(tt, quote! { std::cmp::Eq })
182}
183
184fn partial_eq_expand(
185 _db: &dyn AstDatabase,
186 _id: MacroCallId,
187 tt: &tt::Subtree,
188) -> Result<tt::Subtree, mbe::ExpandError> {
189 expand_simple_derive(tt, quote! { std::cmp::PartialEq })
190}
191
192fn ord_expand(
193 _db: &dyn AstDatabase,
194 _id: MacroCallId,
195 tt: &tt::Subtree,
196) -> Result<tt::Subtree, mbe::ExpandError> {
197 expand_simple_derive(tt, quote! { std::cmp::Ord })
198}
199
200fn partial_ord_expand(
201 _db: &dyn AstDatabase,
202 _id: MacroCallId,
203 tt: &tt::Subtree,
204) -> Result<tt::Subtree, mbe::ExpandError> {
205 expand_simple_derive(tt, quote! { std::cmp::PartialOrd })
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211 use crate::{test_db::TestDB, AstId, MacroCallKind, MacroCallLoc, MacroFileKind};
212 use ra_db::{fixture::WithFixture, SourceDatabase};
213
214 fn expand_builtin_derive(s: &str, expander: BuiltinDeriveExpander) -> String {
215 let (db, file_id) = TestDB::with_single_file(&s);
216 let parsed = db.parse(file_id);
217 let items: Vec<_> =
218 parsed.syntax_node().descendants().filter_map(|it| ast::ModuleItem::cast(it)).collect();
219
220 let ast_id_map = db.ast_id_map(file_id.into());
221
222 // the first one should be a macro_rules
223 let def =
224 MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(expander) };
225
226 let loc = MacroCallLoc {
227 def,
228 kind: MacroCallKind::Attr(AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]))),
229 };
230
231 let id = db.intern_macro(loc);
232 let parsed = db.parse_or_expand(id.as_file(MacroFileKind::Items)).unwrap();
233
234 // FIXME text() for syntax nodes parsed from token tree looks weird
235 // because there's no whitespace, see below
236 parsed.text().to_string()
237 }
238
239 #[test]
240 fn test_copy_expand_simple() {
241 let expanded = expand_builtin_derive(
242 r#"
243 #[derive(Copy)]
244 struct Foo;
245"#,
246 BuiltinDeriveExpander::Copy,
247 );
248
249 assert_eq!(expanded, "impl <>std::marker::CopyforFoo <>{}");
250 }
251
252 #[test]
253 fn test_copy_expand_with_type_params() {
254 let expanded = expand_builtin_derive(
255 r#"
256 #[derive(Copy)]
257 struct Foo<A, B>;
258"#,
259 BuiltinDeriveExpander::Copy,
260 );
261
262 assert_eq!(
263 expanded,
264 "impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}"
265 );
266 }
267
268 #[test]
269 fn test_copy_expand_with_lifetimes() {
270 let expanded = expand_builtin_derive(
271 r#"
272 #[derive(Copy)]
273 struct Foo<A, B, 'a, 'b>;
274"#,
275 BuiltinDeriveExpander::Copy,
276 );
277
278 // We currently just ignore lifetimes
279
280 assert_eq!(
281 expanded,
282 "impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}"
283 );
284 }
285
286 #[test]
287 fn test_clone_expand() {
288 let expanded = expand_builtin_derive(
289 r#"
290 #[derive(Clone)]
291 struct Foo<A, B>;
292"#,
293 BuiltinDeriveExpander::Clone,
294 );
295
296 assert_eq!(
297 expanded,
298 "impl<T0:std::clone::Clone,T1:std::clone::Clone>std::clone::CloneforFoo<T0,T1>{}"
299 );
300 }
301}
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index d370dfb34..35f99b2bc 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -39,7 +39,7 @@ macro_rules! register_builtin {
39 _ => return None, 39 _ => return None,
40 }; 40 };
41 41
42 Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(kind) }) 42 Some(MacroDefId { krate: Some(krate), ast_id: Some(ast_id), kind: MacroDefKind::BuiltIn(kind) })
43 } 43 }
44 }; 44 };
45} 45}
@@ -82,10 +82,9 @@ fn line_expand(
82 _tt: &tt::Subtree, 82 _tt: &tt::Subtree,
83) -> Result<tt::Subtree, mbe::ExpandError> { 83) -> Result<tt::Subtree, mbe::ExpandError> {
84 let loc = db.lookup_intern_macro(id); 84 let loc = db.lookup_intern_macro(id);
85 let macro_call = loc.ast_id.to_node(db);
86 85
87 let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; 86 let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
88 let arg_start = arg.syntax().text_range().start(); 87 let arg_start = arg.text_range().start();
89 88
90 let file = id.as_file(MacroFileKind::Expr); 89 let file = id.as_file(MacroFileKind::Expr);
91 let line_num = to_line_number(db, file, arg_start); 90 let line_num = to_line_number(db, file, arg_start);
@@ -103,11 +102,10 @@ fn stringify_expand(
103 _tt: &tt::Subtree, 102 _tt: &tt::Subtree,
104) -> Result<tt::Subtree, mbe::ExpandError> { 103) -> Result<tt::Subtree, mbe::ExpandError> {
105 let loc = db.lookup_intern_macro(id); 104 let loc = db.lookup_intern_macro(id);
106 let macro_call = loc.ast_id.to_node(db);
107 105
108 let macro_content = { 106 let macro_content = {
109 let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; 107 let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
110 let macro_args = arg.syntax().clone(); 108 let macro_args = arg.clone();
111 let text = macro_args.text(); 109 let text = macro_args.text();
112 let without_parens = TextUnit::of_char('(')..text.len() - TextUnit::of_char(')'); 110 let without_parens = TextUnit::of_char('(')..text.len() - TextUnit::of_char(')');
113 text.slice(without_parens).to_string() 111 text.slice(without_parens).to_string()
@@ -148,7 +146,10 @@ fn column_expand(
148 _tt: &tt::Subtree, 146 _tt: &tt::Subtree,
149) -> Result<tt::Subtree, mbe::ExpandError> { 147) -> Result<tt::Subtree, mbe::ExpandError> {
150 let loc = db.lookup_intern_macro(id); 148 let loc = db.lookup_intern_macro(id);
151 let macro_call = loc.ast_id.to_node(db); 149 let macro_call = match loc.kind {
150 crate::MacroCallKind::FnLike(ast_id) => ast_id.to_node(db),
151 _ => panic!("column macro called as attr"),
152 };
152 153
153 let _arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; 154 let _arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
154 let col_start = macro_call.syntax().text_range().start(); 155 let col_start = macro_call.syntax().text_range().start();
@@ -164,15 +165,10 @@ fn column_expand(
164} 165}
165 166
166fn file_expand( 167fn file_expand(
167 db: &dyn AstDatabase, 168 _db: &dyn AstDatabase,
168 id: MacroCallId, 169 _id: MacroCallId,
169 _tt: &tt::Subtree, 170 _tt: &tt::Subtree,
170) -> Result<tt::Subtree, mbe::ExpandError> { 171) -> Result<tt::Subtree, mbe::ExpandError> {
171 let loc = db.lookup_intern_macro(id);
172 let macro_call = loc.ast_id.to_node(db);
173
174 let _ = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
175
176 // FIXME: RA purposefully lacks knowledge of absolute file names 172 // FIXME: RA purposefully lacks knowledge of absolute file names
177 // so just return "". 173 // so just return "".
178 let file_name = ""; 174 let file_name = "";
@@ -207,7 +203,7 @@ fn compile_error_expand(
207#[cfg(test)] 203#[cfg(test)]
208mod tests { 204mod tests {
209 use super::*; 205 use super::*;
210 use crate::{test_db::TestDB, MacroCallLoc}; 206 use crate::{test_db::TestDB, MacroCallKind, MacroCallLoc};
211 use ra_db::{fixture::WithFixture, SourceDatabase}; 207 use ra_db::{fixture::WithFixture, SourceDatabase};
212 208
213 fn expand_builtin_macro(s: &str, expander: BuiltinFnLikeExpander) -> String { 209 fn expand_builtin_macro(s: &str, expander: BuiltinFnLikeExpander) -> String {
@@ -220,14 +216,17 @@ mod tests {
220 216
221 // the first one should be a macro_rules 217 // the first one should be a macro_rules
222 let def = MacroDefId { 218 let def = MacroDefId {
223 krate: CrateId(0), 219 krate: Some(CrateId(0)),
224 ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0])), 220 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))),
225 kind: MacroDefKind::BuiltIn(expander), 221 kind: MacroDefKind::BuiltIn(expander),
226 }; 222 };
227 223
228 let loc = MacroCallLoc { 224 let loc = MacroCallLoc {
229 def, 225 def,
230 ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[1])), 226 kind: MacroCallKind::FnLike(AstId::new(
227 file_id.into(),
228 ast_id_map.ast_id(&macro_calls[1]),
229 )),
231 }; 230 };
232 231
233 let id = db.intern_macro(loc); 232 let id = db.intern_macro(loc);
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index 8e46fa177..99dabf3fb 100644
--- a/crates/ra_hir_expand/src/db.rs
+++ b/crates/ra_hir_expand/src/db.rs
@@ -9,14 +9,15 @@ use ra_prof::profile;
9use ra_syntax::{AstNode, Parse, SyntaxNode}; 9use ra_syntax::{AstNode, Parse, SyntaxNode};
10 10
11use crate::{ 11use crate::{
12 ast_id_map::AstIdMap, BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, 12 ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, HirFileId, HirFileIdRepr,
13 MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, MacroFileKind, 13 MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, MacroFileKind,
14}; 14};
15 15
16#[derive(Debug, Clone, Eq, PartialEq)] 16#[derive(Debug, Clone, Eq, PartialEq)]
17pub enum TokenExpander { 17pub enum TokenExpander {
18 MacroRules(mbe::MacroRules), 18 MacroRules(mbe::MacroRules),
19 Builtin(BuiltinFnLikeExpander), 19 Builtin(BuiltinFnLikeExpander),
20 BuiltinDerive(BuiltinDeriveExpander),
20} 21}
21 22
22impl TokenExpander { 23impl TokenExpander {
@@ -29,6 +30,7 @@ impl TokenExpander {
29 match self { 30 match self {
30 TokenExpander::MacroRules(it) => it.expand(tt), 31 TokenExpander::MacroRules(it) => it.expand(tt),
31 TokenExpander::Builtin(it) => it.expand(db, id, tt), 32 TokenExpander::Builtin(it) => it.expand(db, id, tt),
33 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt),
32 } 34 }
33 } 35 }
34 36
@@ -36,6 +38,7 @@ impl TokenExpander {
36 match self { 38 match self {
37 TokenExpander::MacroRules(it) => it.map_id_down(id), 39 TokenExpander::MacroRules(it) => it.map_id_down(id),
38 TokenExpander::Builtin(..) => id, 40 TokenExpander::Builtin(..) => id,
41 TokenExpander::BuiltinDerive(..) => id,
39 } 42 }
40 } 43 }
41 44
@@ -43,6 +46,7 @@ impl TokenExpander {
43 match self { 46 match self {
44 TokenExpander::MacroRules(it) => it.map_id_up(id), 47 TokenExpander::MacroRules(it) => it.map_id_up(id),
45 TokenExpander::Builtin(..) => (id, mbe::Origin::Def), 48 TokenExpander::Builtin(..) => (id, mbe::Origin::Def),
49 TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Def),
46 } 50 }
47 } 51 }
48} 52}
@@ -76,7 +80,7 @@ pub(crate) fn macro_def(
76) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { 80) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
77 match id.kind { 81 match id.kind {
78 MacroDefKind::Declarative => { 82 MacroDefKind::Declarative => {
79 let macro_call = id.ast_id.to_node(db); 83 let macro_call = id.ast_id?.to_node(db);
80 let arg = macro_call.token_tree()?; 84 let arg = macro_call.token_tree()?;
81 let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { 85 let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
82 log::warn!("fail on macro_def to token tree: {:#?}", arg); 86 log::warn!("fail on macro_def to token tree: {:#?}", arg);
@@ -91,6 +95,10 @@ pub(crate) fn macro_def(
91 MacroDefKind::BuiltIn(expander) => { 95 MacroDefKind::BuiltIn(expander) => {
92 Some(Arc::new((TokenExpander::Builtin(expander.clone()), mbe::TokenMap::default()))) 96 Some(Arc::new((TokenExpander::Builtin(expander.clone()), mbe::TokenMap::default())))
93 } 97 }
98 MacroDefKind::BuiltInDerive(expander) => Some(Arc::new((
99 TokenExpander::BuiltinDerive(expander.clone()),
100 mbe::TokenMap::default(),
101 ))),
94 } 102 }
95} 103}
96 104
@@ -99,9 +107,8 @@ pub(crate) fn macro_arg(
99 id: MacroCallId, 107 id: MacroCallId,
100) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { 108) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
101 let loc = db.lookup_intern_macro(id); 109 let loc = db.lookup_intern_macro(id);
102 let macro_call = loc.ast_id.to_node(db); 110 let arg = loc.kind.arg(db)?;
103 let arg = macro_call.token_tree()?; 111 let (tt, tmap) = mbe::syntax_node_to_token_tree(&arg)?;
104 let (tt, tmap) = mbe::ast_to_token_tree(&arg)?;
105 Some(Arc::new((tt, tmap))) 112 Some(Arc::new((tt, tmap)))
106} 113}
107 114
diff --git a/crates/ra_hir_expand/src/hygiene.rs b/crates/ra_hir_expand/src/hygiene.rs
index 64c8b06c6..2e8a533f7 100644
--- a/crates/ra_hir_expand/src/hygiene.rs
+++ b/crates/ra_hir_expand/src/hygiene.rs
@@ -25,8 +25,9 @@ impl Hygiene {
25 HirFileIdRepr::MacroFile(macro_file) => { 25 HirFileIdRepr::MacroFile(macro_file) => {
26 let loc = db.lookup_intern_macro(macro_file.macro_call_id); 26 let loc = db.lookup_intern_macro(macro_file.macro_call_id);
27 match loc.def.kind { 27 match loc.def.kind {
28 MacroDefKind::Declarative => Some(loc.def.krate), 28 MacroDefKind::Declarative => loc.def.krate,
29 MacroDefKind::BuiltIn(_) => None, 29 MacroDefKind::BuiltIn(_) => None,
30 MacroDefKind::BuiltInDerive(_) => None,
30 } 31 }
31 } 32 }
32 }; 33 };
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index 3be9bdf86..59c69b91b 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -9,6 +9,7 @@ pub mod ast_id_map;
9pub mod name; 9pub mod name;
10pub mod hygiene; 10pub mod hygiene;
11pub mod diagnostics; 11pub mod diagnostics;
12pub mod builtin_derive;
12pub mod builtin_macro; 13pub mod builtin_macro;
13pub mod quote; 14pub mod quote;
14 15
@@ -23,6 +24,7 @@ use ra_syntax::{
23}; 24};
24 25
25use crate::ast_id_map::FileAstId; 26use crate::ast_id_map::FileAstId;
27use crate::builtin_derive::BuiltinDeriveExpander;
26use crate::builtin_macro::BuiltinFnLikeExpander; 28use crate::builtin_macro::BuiltinFnLikeExpander;
27 29
28#[cfg(test)] 30#[cfg(test)]
@@ -69,7 +71,7 @@ impl HirFileId {
69 HirFileIdRepr::FileId(file_id) => file_id, 71 HirFileIdRepr::FileId(file_id) => file_id,
70 HirFileIdRepr::MacroFile(macro_file) => { 72 HirFileIdRepr::MacroFile(macro_file) => {
71 let loc = db.lookup_intern_macro(macro_file.macro_call_id); 73 let loc = db.lookup_intern_macro(macro_file.macro_call_id);
72 loc.ast_id.file_id.original_file(db) 74 loc.kind.file_id().original_file(db)
73 } 75 }
74 } 76 }
75 } 77 }
@@ -81,8 +83,8 @@ impl HirFileId {
81 HirFileIdRepr::MacroFile(macro_file) => { 83 HirFileIdRepr::MacroFile(macro_file) => {
82 let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); 84 let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
83 85
84 let arg_tt = loc.ast_id.to_node(db).token_tree()?; 86 let arg_tt = loc.kind.arg(db)?;
85 let def_tt = loc.def.ast_id.to_node(db).token_tree()?; 87 let def_tt = loc.def.ast_id?.to_node(db).token_tree()?;
86 88
87 let macro_def = db.macro_def(loc.def)?; 89 let macro_def = db.macro_def(loc.def)?;
88 let (parse, exp_map) = db.parse_macro(macro_file)?; 90 let (parse, exp_map) = db.parse_macro(macro_file)?;
@@ -90,8 +92,8 @@ impl HirFileId {
90 92
91 Some(ExpansionInfo { 93 Some(ExpansionInfo {
92 expanded: InFile::new(self, parse.syntax_node()), 94 expanded: InFile::new(self, parse.syntax_node()),
93 arg: InFile::new(loc.ast_id.file_id, arg_tt), 95 arg: InFile::new(loc.kind.file_id(), arg_tt),
94 def: InFile::new(loc.ast_id.file_id, def_tt), 96 def: InFile::new(loc.def.ast_id?.file_id, def_tt),
95 macro_arg, 97 macro_arg,
96 macro_def, 98 macro_def,
97 exp_map, 99 exp_map,
@@ -129,18 +131,20 @@ impl salsa::InternKey for MacroCallId {
129 131
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 132#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
131pub struct MacroDefId { 133pub struct MacroDefId {
132 pub krate: CrateId, 134 // FIXME: krate and ast_id are currently optional because we don't have a
133 pub ast_id: AstId<ast::MacroCall>, 135 // definition location for built-in derives. There is one, though: the
136 // standard library defines them. The problem is that it uses the new
137 // `macro` syntax for this, which we don't support yet. As soon as we do
138 // (which will probably require touching this code), we can instead use
139 // that (and also remove the hacks for resolving built-in derives).
140 pub krate: Option<CrateId>,
141 pub ast_id: Option<AstId<ast::MacroCall>>,
134 pub kind: MacroDefKind, 142 pub kind: MacroDefKind,
135} 143}
136 144
137impl MacroDefId { 145impl MacroDefId {
138 pub fn as_call_id( 146 pub fn as_call_id(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> MacroCallId {
139 self, 147 db.intern_macro(MacroCallLoc { def: self, kind })
140 db: &dyn db::AstDatabase,
141 ast_id: AstId<ast::MacroCall>,
142 ) -> MacroCallId {
143 db.intern_macro(MacroCallLoc { def: self, ast_id })
144 } 148 }
145} 149}
146 150
@@ -148,12 +152,38 @@ impl MacroDefId {
148pub enum MacroDefKind { 152pub enum MacroDefKind {
149 Declarative, 153 Declarative,
150 BuiltIn(BuiltinFnLikeExpander), 154 BuiltIn(BuiltinFnLikeExpander),
155 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander
156 BuiltInDerive(BuiltinDeriveExpander),
151} 157}
152 158
153#[derive(Debug, Clone, PartialEq, Eq, Hash)] 159#[derive(Debug, Clone, PartialEq, Eq, Hash)]
154pub struct MacroCallLoc { 160pub struct MacroCallLoc {
155 pub(crate) def: MacroDefId, 161 pub(crate) def: MacroDefId,
156 pub(crate) ast_id: AstId<ast::MacroCall>, 162 pub(crate) kind: MacroCallKind,
163}
164
165#[derive(Debug, Clone, PartialEq, Eq, Hash)]
166pub enum MacroCallKind {
167 FnLike(AstId<ast::MacroCall>),
168 Attr(AstId<ast::ModuleItem>),
169}
170
171impl MacroCallKind {
172 pub fn file_id(&self) -> HirFileId {
173 match self {
174 MacroCallKind::FnLike(ast_id) => ast_id.file_id,
175 MacroCallKind::Attr(ast_id) => ast_id.file_id,
176 }
177 }
178
179 pub fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> {
180 match self {
181 MacroCallKind::FnLike(ast_id) => {
182 Some(ast_id.to_node(db).token_tree()?.syntax().clone())
183 }
184 MacroCallKind::Attr(ast_id) => Some(ast_id.to_node(db).syntax().clone()),
185 }
186 }
157} 187}
158 188
159impl MacroCallId { 189impl MacroCallId {
@@ -167,7 +197,7 @@ impl MacroCallId {
167#[derive(Debug, Clone, PartialEq, Eq)] 197#[derive(Debug, Clone, PartialEq, Eq)]
168pub struct ExpansionInfo { 198pub struct ExpansionInfo {
169 expanded: InFile<SyntaxNode>, 199 expanded: InFile<SyntaxNode>,
170 arg: InFile<ast::TokenTree>, 200 arg: InFile<SyntaxNode>,
171 def: InFile<ast::TokenTree>, 201 def: InFile<ast::TokenTree>,
172 202
173 macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, 203 macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>,
@@ -178,8 +208,7 @@ pub struct ExpansionInfo {
178impl ExpansionInfo { 208impl ExpansionInfo {
179 pub fn map_token_down(&self, token: InFile<&SyntaxToken>) -> Option<InFile<SyntaxToken>> { 209 pub fn map_token_down(&self, token: InFile<&SyntaxToken>) -> Option<InFile<SyntaxToken>> {
180 assert_eq!(token.file_id, self.arg.file_id); 210 assert_eq!(token.file_id, self.arg.file_id);
181 let range = 211 let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
182 token.value.text_range().checked_sub(self.arg.value.syntax().text_range().start())?;
183 let token_id = self.macro_arg.1.token_by_range(range)?; 212 let token_id = self.macro_arg.1.token_by_range(range)?;
184 let token_id = self.macro_def.0.map_id_down(token_id); 213 let token_id = self.macro_def.0.map_id_down(token_id);
185 214
@@ -195,16 +224,15 @@ impl ExpansionInfo {
195 224
196 let (token_id, origin) = self.macro_def.0.map_id_up(token_id); 225 let (token_id, origin) = self.macro_def.0.map_id_up(token_id);
197 let (token_map, tt) = match origin { 226 let (token_map, tt) = match origin {
198 mbe::Origin::Call => (&self.macro_arg.1, &self.arg), 227 mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()),
199 mbe::Origin::Def => (&self.macro_def.1, &self.def), 228 mbe::Origin::Def => {
229 (&self.macro_def.1, self.def.as_ref().map(|tt| tt.syntax().clone()))
230 }
200 }; 231 };
201 232
202 let range = token_map.range_by_token(token_id)?; 233 let range = token_map.range_by_token(token_id)?;
203 let token = algo::find_covering_element( 234 let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start())
204 tt.value.syntax(), 235 .into_token()?;
205 range + tt.value.syntax().text_range().start(),
206 )
207 .into_token()?;
208 Some(tt.with_value(token)) 236 Some(tt.with_value(token))
209 } 237 }
210} 238}
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index 05ba37070..c5a191160 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -83,6 +83,12 @@ impl AsName for ast::Name {
83 } 83 }
84} 84}
85 85
86impl AsName for tt::Ident {
87 fn as_name(&self) -> Name {
88 Name::resolve(&self.text)
89 }
90}
91
86impl AsName for ast::FieldKind { 92impl AsName for ast::FieldKind {
87 fn as_name(&self) -> Name { 93 fn as_name(&self) -> Name {
88 match self { 94 match self {
@@ -153,3 +159,14 @@ pub const COLUMN_MACRO: Name = Name::new_inline_ascii(6, b"column");
153pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(13, b"compile_error"); 159pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(13, b"compile_error");
154pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line"); 160pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line");
155pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(9, b"stringify"); 161pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(9, b"stringify");
162
163// Builtin derives
164pub const COPY_TRAIT: Name = Name::new_inline_ascii(4, b"Copy");
165pub const CLONE_TRAIT: Name = Name::new_inline_ascii(5, b"Clone");
166pub const DEFAULT_TRAIT: Name = Name::new_inline_ascii(7, b"Default");
167pub const DEBUG_TRAIT: Name = Name::new_inline_ascii(5, b"Debug");
168pub const HASH_TRAIT: Name = Name::new_inline_ascii(4, b"Hash");
169pub const ORD_TRAIT: Name = Name::new_inline_ascii(3, b"Ord");
170pub const PARTIAL_ORD_TRAIT: Name = Name::new_inline_ascii(10, b"PartialOrd");
171pub const EQ_TRAIT: Name = Name::new_inline_ascii(2, b"Eq");
172pub const PARTIAL_EQ_TRAIT: Name = Name::new_inline_ascii(9, b"PartialEq");
diff --git a/crates/ra_hir_expand/src/quote.rs b/crates/ra_hir_expand/src/quote.rs
index 65a35e52f..4f698ff13 100644
--- a/crates/ra_hir_expand/src/quote.rs
+++ b/crates/ra_hir_expand/src/quote.rs
@@ -60,6 +60,15 @@ macro_rules! __quote {
60 } 60 }
61 }; 61 };
62 62
63 ( ## $first:ident $($tail:tt)* ) => {
64 {
65 let mut tokens = $first.into_iter().map($crate::quote::ToTokenTree::to_token).collect::<Vec<tt::TokenTree>>();
66 let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
67 tokens.append(&mut tail_tokens);
68 tokens
69 }
70 };
71
63 // Brace 72 // Brace
64 ( { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE Brace $($tt)*) }; 73 ( { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE Brace $($tt)*) };
65 // Bracket 74 // Bracket
@@ -85,6 +94,7 @@ macro_rules! __quote {
85 ( & ) => {$crate::__quote!(@PUNCT '&')}; 94 ( & ) => {$crate::__quote!(@PUNCT '&')};
86 ( , ) => {$crate::__quote!(@PUNCT ',')}; 95 ( , ) => {$crate::__quote!(@PUNCT ',')};
87 ( : ) => {$crate::__quote!(@PUNCT ':')}; 96 ( : ) => {$crate::__quote!(@PUNCT ':')};
97 ( :: ) => {$crate::__quote!(@PUNCT ':', ':')};
88 ( . ) => {$crate::__quote!(@PUNCT '.')}; 98 ( . ) => {$crate::__quote!(@PUNCT '.')};
89 99
90 ( $first:tt $($tail:tt)+ ) => { 100 ( $first:tt $($tail:tt)+ ) => {
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs
index 0d9a35ce0..9c29a054e 100644
--- a/crates/ra_hir_ty/src/tests/macros.rs
+++ b/crates/ra_hir_ty/src/tests/macros.rs
@@ -266,3 +266,54 @@ fn main() {
266 "### 266 "###
267 ); 267 );
268} 268}
269
270#[test]
271fn infer_derive_clone_simple() {
272 let (db, pos) = TestDB::with_position(
273 r#"
274//- /main.rs crate:main deps:std
275#[derive(Clone)]
276struct S;
277fn test() {
278 S.clone()<|>;
279}
280
281//- /lib.rs crate:std
282#[prelude_import]
283use clone::*;
284mod clone {
285 trait Clone {
286 fn clone(&self) -> Self;
287 }
288}
289"#,
290 );
291 assert_eq!("S", type_at_pos(&db, pos));
292}
293
294#[test]
295fn infer_derive_clone_with_params() {
296 let (db, pos) = TestDB::with_position(
297 r#"
298//- /main.rs crate:main deps:std
299#[derive(Clone)]
300struct S;
301#[derive(Clone)]
302struct Wrapper<T>(T);
303struct NonClone;
304fn test() {
305 (Wrapper(S).clone(), Wrapper(NonClone).clone())<|>;
306}
307
308//- /lib.rs crate:std
309#[prelude_import]
310use clone::*;
311mod clone {
312 trait Clone {
313 fn clone(&self) -> Self;
314 }
315}
316"#,
317 );
318 assert_eq!("(Wrapper<S>, {unknown})", type_at_pos(&db, pos));
319}
diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs
index 1de399fee..66c1f0337 100644
--- a/crates/ra_mbe/src/syntax_bridge.rs
+++ b/crates/ra_mbe/src/syntax_bridge.rs
@@ -2,7 +2,7 @@
2 2
3use ra_parser::{FragmentKind, ParseError, TreeSink}; 3use ra_parser::{FragmentKind, ParseError, TreeSink};
4use ra_syntax::{ 4use ra_syntax::{
5 ast, AstNode, AstToken, NodeOrToken, Parse, SmolStr, SyntaxKind, SyntaxKind::*, SyntaxNode, 5 ast, AstToken, NodeOrToken, Parse, SmolStr, SyntaxKind, SyntaxKind::*, SyntaxNode,
6 SyntaxTreeBuilder, TextRange, TextUnit, T, 6 SyntaxTreeBuilder, TextRange, TextUnit, T,
7}; 7};
8use std::iter::successors; 8use std::iter::successors;
@@ -20,7 +20,7 @@ pub struct TokenMap {
20 20
21/// Convert the syntax tree (what user has written) to a `TokenTree` (what macro 21/// Convert the syntax tree (what user has written) to a `TokenTree` (what macro
22/// will consume). 22/// will consume).
23pub fn ast_to_token_tree(ast: &ast::TokenTree) -> Option<(tt::Subtree, TokenMap)> { 23pub fn ast_to_token_tree(ast: &impl ast::AstNode) -> Option<(tt::Subtree, TokenMap)> {
24 syntax_node_to_token_tree(ast.syntax()) 24 syntax_node_to_token_tree(ast.syntax())
25} 25}
26 26
@@ -208,13 +208,8 @@ impl Convertor {
208 } else if token.kind().is_trivia() { 208 } else if token.kind().is_trivia() {
209 continue; 209 continue;
210 } else if token.kind().is_punct() { 210 } else if token.kind().is_punct() {
211 assert!( 211 // we need to pull apart joined punctuation tokens
212 token.text().len() == 1, 212 let last_spacing = match child_iter.peek() {
213 "Input ast::token punct must be single char."
214 );
215 let char = token.text().chars().next().unwrap();
216
217 let spacing = match child_iter.peek() {
218 Some(NodeOrToken::Token(token)) => { 213 Some(NodeOrToken::Token(token)) => {
219 if token.kind().is_punct() { 214 if token.kind().is_punct() {
220 tt::Spacing::Joint 215 tt::Spacing::Joint
@@ -224,8 +219,12 @@ impl Convertor {
224 } 219 }
225 _ => tt::Spacing::Alone, 220 _ => tt::Spacing::Alone,
226 }; 221 };
227 222 let spacing_iter = std::iter::repeat(tt::Spacing::Joint)
228 token_trees.push(tt::Leaf::from(tt::Punct { char, spacing }).into()); 223 .take(token.text().len() - 1)
224 .chain(std::iter::once(last_spacing));
225 for (char, spacing) in token.text().chars().zip(spacing_iter) {
226 token_trees.push(tt::Leaf::from(tt::Punct { char, spacing }).into());
227 }
229 } else { 228 } else {
230 let child: tt::TokenTree = 229 let child: tt::TokenTree =
231 if token.kind() == T![true] || token.kind() == T![false] { 230 if token.kind() == T![true] || token.kind() == T![false] {
@@ -246,8 +245,14 @@ impl Convertor {
246 } 245 }
247 } 246 }
248 NodeOrToken::Node(node) => { 247 NodeOrToken::Node(node) => {
249 let child = self.go(&node)?.into(); 248 let child_subtree = self.go(&node)?;
250 token_trees.push(child); 249 if child_subtree.delimiter == tt::Delimiter::None
250 && node.kind() != SyntaxKind::TOKEN_TREE
251 {
252 token_trees.extend(child_subtree.token_trees);
253 } else {
254 token_trees.push(child_subtree.into());
255 }
251 } 256 }
252 }; 257 };
253 } 258 }
@@ -389,7 +394,10 @@ mod tests {
389 use super::*; 394 use super::*;
390 use crate::tests::{create_rules, expand}; 395 use crate::tests::{create_rules, expand};
391 use ra_parser::TokenSource; 396 use ra_parser::TokenSource;
392 use ra_syntax::algo::{insert_children, InsertPosition}; 397 use ra_syntax::{
398 algo::{insert_children, InsertPosition},
399 ast::AstNode,
400 };
393 401
394 #[test] 402 #[test]
395 fn convert_tt_token_source() { 403 fn convert_tt_token_source() {
@@ -491,4 +499,12 @@ mod tests {
491 499
492 assert_eq!(tt.delimiter, tt::Delimiter::Brace); 500 assert_eq!(tt.delimiter, tt::Delimiter::Brace);
493 } 501 }
502
503 #[test]
504 fn test_token_tree_multi_char_punct() {
505 let source_file = ast::SourceFile::parse("struct Foo { a: x::Y }").ok().unwrap();
506 let struct_def = source_file.syntax().descendants().find_map(ast::StructDef::cast).unwrap();
507 let tt = ast_to_token_tree(&struct_def).unwrap().0;
508 token_tree_to_syntax_node(&tt, FragmentKind::Item).unwrap();
509 }
494} 510}