diff options
Diffstat (limited to 'crates')
23 files changed, 714 insertions, 105 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 { | |||
105 | impl HasSource for MacroDef { | 105 | impl 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 | } |
111 | impl HasSource for ImplBlock { | 114 | impl 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 | }; |
22 | use hir_expand::{ | 22 | use 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 | }; |
25 | use ra_syntax::{ | 26 | use 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; | |||
6 | use std::{ops::Index, sync::Arc}; | 6 | use std::{ops::Index, sync::Arc}; |
7 | 7 | ||
8 | use either::Either; | 8 | use either::Either; |
9 | use hir_expand::{hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId, MacroFileKind}; | 9 | use hir_expand::{ |
10 | hygiene::Hygiene, AstId, HirFileId, InFile, MacroCallKind, MacroDefId, MacroFileKind, | ||
11 | }; | ||
10 | use ra_arena::{map::ArenaMap, Arena}; | 12 | use ra_arena::{map::ArenaMap, Arena}; |
11 | use ra_syntax::{ast, AstNode, AstPtr}; | 13 | use ra_syntax::{ast, AstNode, AstPtr}; |
12 | use rustc_hash::FxHashMap; | 14 | use 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 | ||
6 | use hir_expand::{ | 6 | use 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 | }; |
11 | use ra_cfg::CfgOptions; | 12 | use ra_cfg::CfgOptions; |
12 | use ra_db::{CrateId, FileId}; | 13 | use 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 | ||
187 | impl 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)] |
188 | pub(super) struct Macro(RawId); | 203 | pub(super) struct Macro(RawId); |
189 | impl_arena_id!(Macro); | 204 | impl_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] | ||
605 | fn 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] | ||
617 | fn 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 | ||
42 | impl<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)] |
43 | struct ErasedFileAstId(RawId); | 53 | struct ErasedFileAstId(RawId); |
44 | impl_arena_id!(ErasedFileAstId); | 54 | impl_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 | |||
3 | use log::debug; | ||
4 | |||
5 | use ra_parser::FragmentKind; | ||
6 | use ra_syntax::{ | ||
7 | ast::{self, AstNode, ModuleItemOwner, NameOwner, TypeParamsOwner}, | ||
8 | match_ast, | ||
9 | }; | ||
10 | |||
11 | use crate::db::AstDatabase; | ||
12 | use crate::{name, quote, MacroCallId, MacroDefId, MacroDefKind}; | ||
13 | |||
14 | macro_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 | |||
46 | register_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 | |||
58 | struct BasicAdtInfo { | ||
59 | name: tt::Ident, | ||
60 | type_params: usize, | ||
61 | } | ||
62 | |||
63 | fn 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 | |||
98 | fn 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 | |||
119 | fn 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 | |||
136 | fn 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 | |||
144 | fn 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 | |||
152 | fn 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 | |||
160 | fn 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 | |||
168 | fn 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 | |||
176 | fn 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 | |||
184 | fn 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 | |||
192 | fn 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 | |||
200 | fn 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)] | ||
209 | mod 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 | ||
166 | fn file_expand( | 167 | fn 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)] |
208 | mod tests { | 204 | mod 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(¯o_calls[0])), | 220 | ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_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(¯o_calls[1])), | 226 | kind: MacroCallKind::FnLike(AstId::new( |
227 | file_id.into(), | ||
228 | ast_id_map.ast_id(¯o_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; | |||
9 | use ra_syntax::{AstNode, Parse, SyntaxNode}; | 9 | use ra_syntax::{AstNode, Parse, SyntaxNode}; |
10 | 10 | ||
11 | use crate::{ | 11 | use 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)] |
17 | pub enum TokenExpander { | 17 | pub enum TokenExpander { |
18 | MacroRules(mbe::MacroRules), | 18 | MacroRules(mbe::MacroRules), |
19 | Builtin(BuiltinFnLikeExpander), | 19 | Builtin(BuiltinFnLikeExpander), |
20 | BuiltinDerive(BuiltinDeriveExpander), | ||
20 | } | 21 | } |
21 | 22 | ||
22 | impl TokenExpander { | 23 | impl 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; | |||
9 | pub mod name; | 9 | pub mod name; |
10 | pub mod hygiene; | 10 | pub mod hygiene; |
11 | pub mod diagnostics; | 11 | pub mod diagnostics; |
12 | pub mod builtin_derive; | ||
12 | pub mod builtin_macro; | 13 | pub mod builtin_macro; |
13 | pub mod quote; | 14 | pub mod quote; |
14 | 15 | ||
@@ -23,6 +24,7 @@ use ra_syntax::{ | |||
23 | }; | 24 | }; |
24 | 25 | ||
25 | use crate::ast_id_map::FileAstId; | 26 | use crate::ast_id_map::FileAstId; |
27 | use crate::builtin_derive::BuiltinDeriveExpander; | ||
26 | use crate::builtin_macro::BuiltinFnLikeExpander; | 28 | use 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)] |
131 | pub struct MacroDefId { | 133 | pub 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 | ||
137 | impl MacroDefId { | 145 | impl 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 { | |||
148 | pub enum MacroDefKind { | 152 | pub 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)] |
154 | pub struct MacroCallLoc { | 160 | pub 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)] | ||
166 | pub enum MacroCallKind { | ||
167 | FnLike(AstId<ast::MacroCall>), | ||
168 | Attr(AstId<ast::ModuleItem>), | ||
169 | } | ||
170 | |||
171 | impl 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 | ||
159 | impl MacroCallId { | 189 | impl MacroCallId { |
@@ -167,7 +197,7 @@ impl MacroCallId { | |||
167 | #[derive(Debug, Clone, PartialEq, Eq)] | 197 | #[derive(Debug, Clone, PartialEq, Eq)] |
168 | pub struct ExpansionInfo { | 198 | pub 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 { | |||
178 | impl ExpansionInfo { | 208 | impl 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 | ||
86 | impl AsName for tt::Ident { | ||
87 | fn as_name(&self) -> Name { | ||
88 | Name::resolve(&self.text) | ||
89 | } | ||
90 | } | ||
91 | |||
86 | impl AsName for ast::FieldKind { | 92 | impl 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"); | |||
153 | pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(13, b"compile_error"); | 159 | pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(13, b"compile_error"); |
154 | pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line"); | 160 | pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line"); |
155 | pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(9, b"stringify"); | 161 | pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(9, b"stringify"); |
162 | |||
163 | // Builtin derives | ||
164 | pub const COPY_TRAIT: Name = Name::new_inline_ascii(4, b"Copy"); | ||
165 | pub const CLONE_TRAIT: Name = Name::new_inline_ascii(5, b"Clone"); | ||
166 | pub const DEFAULT_TRAIT: Name = Name::new_inline_ascii(7, b"Default"); | ||
167 | pub const DEBUG_TRAIT: Name = Name::new_inline_ascii(5, b"Debug"); | ||
168 | pub const HASH_TRAIT: Name = Name::new_inline_ascii(4, b"Hash"); | ||
169 | pub const ORD_TRAIT: Name = Name::new_inline_ascii(3, b"Ord"); | ||
170 | pub const PARTIAL_ORD_TRAIT: Name = Name::new_inline_ascii(10, b"PartialOrd"); | ||
171 | pub const EQ_TRAIT: Name = Name::new_inline_ascii(2, b"Eq"); | ||
172 | pub 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/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 1e78f6efd..b8df27706 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs | |||
@@ -201,7 +201,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
201 | } | 201 | } |
202 | Expr::Return { expr } => { | 202 | Expr::Return { expr } => { |
203 | if let Some(expr) = expr { | 203 | if let Some(expr) = expr { |
204 | self.infer_expr(*expr, &Expectation::has_type(self.return_ty.clone())); | 204 | self.infer_expr_coerce(*expr, &Expectation::has_type(self.return_ty.clone())); |
205 | } | 205 | } |
206 | Ty::simple(TypeCtor::Never) | 206 | Ty::simple(TypeCtor::Never) |
207 | } | 207 | } |
@@ -245,7 +245,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
245 | ty | 245 | ty |
246 | } | 246 | } |
247 | Expr::Field { expr, name } => { | 247 | Expr::Field { expr, name } => { |
248 | let receiver_ty = self.infer_expr(*expr, &Expectation::none()); | 248 | let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none()); |
249 | let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty); | 249 | let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty); |
250 | let ty = autoderef::autoderef( | 250 | let ty = autoderef::autoderef( |
251 | self.db, | 251 | self.db, |
@@ -280,7 +280,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
280 | self.normalize_associated_types_in(ty) | 280 | self.normalize_associated_types_in(ty) |
281 | } | 281 | } |
282 | Expr::Await { expr } => { | 282 | Expr::Await { expr } => { |
283 | let inner_ty = self.infer_expr(*expr, &Expectation::none()); | 283 | let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); |
284 | let ty = match self.resolve_future_future_output() { | 284 | let ty = match self.resolve_future_future_output() { |
285 | Some(future_future_output_alias) => { | 285 | Some(future_future_output_alias) => { |
286 | let ty = self.table.new_type_var(); | 286 | let ty = self.table.new_type_var(); |
@@ -299,7 +299,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
299 | ty | 299 | ty |
300 | } | 300 | } |
301 | Expr::Try { expr } => { | 301 | Expr::Try { expr } => { |
302 | let inner_ty = self.infer_expr(*expr, &Expectation::none()); | 302 | let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); |
303 | let ty = match self.resolve_ops_try_ok() { | 303 | let ty = match self.resolve_ops_try_ok() { |
304 | Some(ops_try_ok_alias) => { | 304 | Some(ops_try_ok_alias) => { |
305 | let ty = self.table.new_type_var(); | 305 | let ty = self.table.new_type_var(); |
@@ -318,7 +318,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
318 | ty | 318 | ty |
319 | } | 319 | } |
320 | Expr::Cast { expr, type_ref } => { | 320 | Expr::Cast { expr, type_ref } => { |
321 | let _inner_ty = self.infer_expr(*expr, &Expectation::none()); | 321 | let _inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); |
322 | let cast_ty = self.make_ty(type_ref); | 322 | let cast_ty = self.make_ty(type_ref); |
323 | // FIXME check the cast... | 323 | // FIXME check the cast... |
324 | cast_ty | 324 | cast_ty |
@@ -334,12 +334,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
334 | } else { | 334 | } else { |
335 | Expectation::none() | 335 | Expectation::none() |
336 | }; | 336 | }; |
337 | // FIXME reference coercions etc. | 337 | let inner_ty = self.infer_expr_inner(*expr, &expectation); |
338 | let inner_ty = self.infer_expr(*expr, &expectation); | ||
339 | Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty) | 338 | Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty) |
340 | } | 339 | } |
341 | Expr::Box { expr } => { | 340 | Expr::Box { expr } => { |
342 | let inner_ty = self.infer_expr(*expr, &Expectation::none()); | 341 | let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); |
343 | if let Some(box_) = self.resolve_boxed_box() { | 342 | if let Some(box_) = self.resolve_boxed_box() { |
344 | Ty::apply_one(TypeCtor::Adt(box_), inner_ty) | 343 | Ty::apply_one(TypeCtor::Adt(box_), inner_ty) |
345 | } else { | 344 | } else { |
@@ -347,7 +346,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
347 | } | 346 | } |
348 | } | 347 | } |
349 | Expr::UnaryOp { expr, op } => { | 348 | Expr::UnaryOp { expr, op } => { |
350 | let inner_ty = self.infer_expr(*expr, &Expectation::none()); | 349 | let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); |
351 | match op { | 350 | match op { |
352 | UnaryOp::Deref => match self.resolver.krate() { | 351 | UnaryOp::Deref => match self.resolver.krate() { |
353 | Some(krate) => { | 352 | Some(krate) => { |
@@ -417,7 +416,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
417 | _ => Ty::Unknown, | 416 | _ => Ty::Unknown, |
418 | }, | 417 | }, |
419 | Expr::Range { lhs, rhs, range_type } => { | 418 | Expr::Range { lhs, rhs, range_type } => { |
420 | let lhs_ty = lhs.map(|e| self.infer_expr(e, &Expectation::none())); | 419 | let lhs_ty = lhs.map(|e| self.infer_expr_inner(e, &Expectation::none())); |
421 | let rhs_expect = lhs_ty | 420 | let rhs_expect = lhs_ty |
422 | .as_ref() | 421 | .as_ref() |
423 | .map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone())); | 422 | .map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone())); |
@@ -455,7 +454,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
455 | } | 454 | } |
456 | } | 455 | } |
457 | Expr::Index { base, index } => { | 456 | Expr::Index { base, index } => { |
458 | let _base_ty = self.infer_expr(*base, &Expectation::none()); | 457 | let _base_ty = self.infer_expr_inner(*base, &Expectation::none()); |
459 | let _index_ty = self.infer_expr(*index, &Expectation::none()); | 458 | let _index_ty = self.infer_expr(*index, &Expectation::none()); |
460 | // FIXME: use `std::ops::Index::Output` to figure out the real return type | 459 | // FIXME: use `std::ops::Index::Output` to figure out the real return type |
461 | Ty::Unknown | 460 | Ty::Unknown |
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index c520bb375..f1b67555f 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs | |||
@@ -11,8 +11,8 @@ use std::fmt::Write; | |||
11 | use std::sync::Arc; | 11 | use std::sync::Arc; |
12 | 12 | ||
13 | use hir_def::{ | 13 | use hir_def::{ |
14 | body::BodySourceMap, db::DefDatabase, nameres::CrateDefMap, AssocItemId, DefWithBodyId, | 14 | body::BodySourceMap, child_from_source::ChildFromSource, db::DefDatabase, nameres::CrateDefMap, |
15 | LocalModuleId, Lookup, ModuleDefId, | 15 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, |
16 | }; | 16 | }; |
17 | use hir_expand::InFile; | 17 | use hir_expand::InFile; |
18 | use insta::assert_snapshot; | 18 | use insta::assert_snapshot; |
@@ -31,18 +31,15 @@ use crate::{db::HirDatabase, display::HirDisplay, test_db::TestDB, InferenceResu | |||
31 | fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { | 31 | fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { |
32 | let file = db.parse(pos.file_id).ok().unwrap(); | 32 | let file = db.parse(pos.file_id).ok().unwrap(); |
33 | let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); | 33 | let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); |
34 | 34 | let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); | |
35 | let module = db.module_for_file(pos.file_id); | 35 | let module = db.module_for_file(pos.file_id); |
36 | let crate_def_map = db.crate_def_map(module.krate); | 36 | let func = module.child_from_source(db, InFile::new(pos.file_id.into(), fn_def)).unwrap(); |
37 | for decl in crate_def_map[module.local_id].scope.declarations() { | 37 | |
38 | if let ModuleDefId::FunctionId(func) = decl { | 38 | let (_body, source_map) = db.body_with_source_map(func.into()); |
39 | let (_body, source_map) = db.body_with_source_map(func.into()); | 39 | if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) { |
40 | if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) { | 40 | let infer = db.infer(func.into()); |
41 | let infer = db.infer(func.into()); | 41 | let ty = &infer[expr_id]; |
42 | let ty = &infer[expr_id]; | 42 | return ty.display(db).to_string(); |
43 | return ty.display(db).to_string(); | ||
44 | } | ||
45 | } | ||
46 | } | 43 | } |
47 | panic!("Can't find expression") | 44 | panic!("Can't find expression") |
48 | } | 45 | } |
@@ -53,6 +50,10 @@ fn type_at(content: &str) -> String { | |||
53 | } | 50 | } |
54 | 51 | ||
55 | fn infer(content: &str) -> String { | 52 | fn infer(content: &str) -> String { |
53 | infer_with_mismatches(content, false) | ||
54 | } | ||
55 | |||
56 | fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { | ||
56 | let (db, file_id) = TestDB::with_single_file(content); | 57 | let (db, file_id) = TestDB::with_single_file(content); |
57 | 58 | ||
58 | let mut acc = String::new(); | 59 | let mut acc = String::new(); |
@@ -60,6 +61,7 @@ fn infer(content: &str) -> String { | |||
60 | let mut infer_def = |inference_result: Arc<InferenceResult>, | 61 | let mut infer_def = |inference_result: Arc<InferenceResult>, |
61 | body_source_map: Arc<BodySourceMap>| { | 62 | body_source_map: Arc<BodySourceMap>| { |
62 | let mut types = Vec::new(); | 63 | let mut types = Vec::new(); |
64 | let mut mismatches = Vec::new(); | ||
63 | 65 | ||
64 | for (pat, ty) in inference_result.type_of_pat.iter() { | 66 | for (pat, ty) in inference_result.type_of_pat.iter() { |
65 | let syntax_ptr = match body_source_map.pat_syntax(pat) { | 67 | let syntax_ptr = match body_source_map.pat_syntax(pat) { |
@@ -79,6 +81,9 @@ fn infer(content: &str) -> String { | |||
79 | None => continue, | 81 | None => continue, |
80 | }; | 82 | }; |
81 | types.push((syntax_ptr, ty)); | 83 | types.push((syntax_ptr, ty)); |
84 | if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) { | ||
85 | mismatches.push((syntax_ptr, mismatch)); | ||
86 | } | ||
82 | } | 87 | } |
83 | 88 | ||
84 | // sort ranges for consistency | 89 | // sort ranges for consistency |
@@ -104,6 +109,24 @@ fn infer(content: &str) -> String { | |||
104 | ) | 109 | ) |
105 | .unwrap(); | 110 | .unwrap(); |
106 | } | 111 | } |
112 | if include_mismatches { | ||
113 | mismatches.sort_by_key(|(src_ptr, _)| { | ||
114 | (src_ptr.value.range().start(), src_ptr.value.range().end()) | ||
115 | }); | ||
116 | for (src_ptr, mismatch) in &mismatches { | ||
117 | let range = src_ptr.value.range(); | ||
118 | let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" }; | ||
119 | write!( | ||
120 | acc, | ||
121 | "{}{}: expected {}, got {}\n", | ||
122 | macro_prefix, | ||
123 | range, | ||
124 | mismatch.expected.display(&db), | ||
125 | mismatch.actual.display(&db), | ||
126 | ) | ||
127 | .unwrap(); | ||
128 | } | ||
129 | } | ||
107 | }; | 130 | }; |
108 | 131 | ||
109 | let module = db.module_for_file(file_id); | 132 | let module = db.module_for_file(file_id); |
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index 1530fcc63..58b22396f 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs | |||
@@ -1,3 +1,4 @@ | |||
1 | use super::infer_with_mismatches; | ||
1 | use insta::assert_snapshot; | 2 | use insta::assert_snapshot; |
2 | use test_utils::covers; | 3 | use test_utils::covers; |
3 | 4 | ||
@@ -367,3 +368,38 @@ fn test() { | |||
367 | "### | 368 | "### |
368 | ); | 369 | ); |
369 | } | 370 | } |
371 | |||
372 | #[test] | ||
373 | fn coerce_autoderef() { | ||
374 | assert_snapshot!( | ||
375 | infer_with_mismatches(r#" | ||
376 | struct Foo; | ||
377 | fn takes_ref_foo(x: &Foo) {} | ||
378 | fn test() { | ||
379 | takes_ref_foo(&Foo); | ||
380 | takes_ref_foo(&&Foo); | ||
381 | takes_ref_foo(&&&Foo); | ||
382 | } | ||
383 | "#, true), | ||
384 | @r###" | ||
385 | [30; 31) 'x': &Foo | ||
386 | [39; 41) '{}': () | ||
387 | [52; 133) '{ ...oo); }': () | ||
388 | [58; 71) 'takes_ref_foo': fn takes_ref_foo(&Foo) -> () | ||
389 | [58; 77) 'takes_...(&Foo)': () | ||
390 | [72; 76) '&Foo': &Foo | ||
391 | [73; 76) 'Foo': Foo | ||
392 | [83; 96) 'takes_ref_foo': fn takes_ref_foo(&Foo) -> () | ||
393 | [83; 103) 'takes_...&&Foo)': () | ||
394 | [97; 102) '&&Foo': &&Foo | ||
395 | [98; 102) '&Foo': &Foo | ||
396 | [99; 102) 'Foo': Foo | ||
397 | [109; 122) 'takes_ref_foo': fn takes_ref_foo(&Foo) -> () | ||
398 | [109; 130) 'takes_...&&Foo)': () | ||
399 | [123; 129) '&&&Foo': &&&Foo | ||
400 | [124; 129) '&&Foo': &&Foo | ||
401 | [125; 129) '&Foo': &Foo | ||
402 | [126; 129) 'Foo': Foo | ||
403 | "### | ||
404 | ); | ||
405 | } | ||
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] | ||
271 | fn infer_derive_clone_simple() { | ||
272 | let (db, pos) = TestDB::with_position( | ||
273 | r#" | ||
274 | //- /main.rs crate:main deps:std | ||
275 | #[derive(Clone)] | ||
276 | struct S; | ||
277 | fn test() { | ||
278 | S.clone()<|>; | ||
279 | } | ||
280 | |||
281 | //- /lib.rs crate:std | ||
282 | #[prelude_import] | ||
283 | use clone::*; | ||
284 | mod 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] | ||
295 | fn infer_derive_clone_with_params() { | ||
296 | let (db, pos) = TestDB::with_position( | ||
297 | r#" | ||
298 | //- /main.rs crate:main deps:std | ||
299 | #[derive(Clone)] | ||
300 | struct S; | ||
301 | #[derive(Clone)] | ||
302 | struct Wrapper<T>(T); | ||
303 | struct NonClone; | ||
304 | fn test() { | ||
305 | (Wrapper(S).clone(), Wrapper(NonClone).clone())<|>; | ||
306 | } | ||
307 | |||
308 | //- /lib.rs crate:std | ||
309 | #[prelude_import] | ||
310 | use clone::*; | ||
311 | mod 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 | ||
3 | use ra_parser::{FragmentKind, ParseError, TreeSink}; | 3 | use ra_parser::{FragmentKind, ParseError, TreeSink}; |
4 | use ra_syntax::{ | 4 | use 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 | }; |
8 | use std::iter::successors; | 8 | use 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). |
23 | pub fn ast_to_token_tree(ast: &ast::TokenTree) -> Option<(tt::Subtree, TokenMap)> { | 23 | pub 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 | } |