diff options
Diffstat (limited to 'crates/ra_hir/src/macros.rs')
-rw-r--r-- | crates/ra_hir/src/macros.rs | 135 |
1 files changed, 0 insertions, 135 deletions
diff --git a/crates/ra_hir/src/macros.rs b/crates/ra_hir/src/macros.rs deleted file mode 100644 index 45128c7df..000000000 --- a/crates/ra_hir/src/macros.rs +++ /dev/null | |||
@@ -1,135 +0,0 @@ | |||
1 | /// Machinery for macro expansion. | ||
2 | /// | ||
3 | /// One of the more complicated things about macros is managing the source code | ||
4 | /// that is produced after expansion. See `HirFileId` and `MacroCallId` for how | ||
5 | /// do we do that. | ||
6 | /// | ||
7 | /// When the file-management question is resolved, all that is left is a | ||
8 | /// token-tree-to-token-tree transformation plus hygiene. We don't have either of | ||
9 | /// those yet, so all macros are string based at the moment! | ||
10 | use std::sync::Arc; | ||
11 | |||
12 | use ra_syntax::{ | ||
13 | TextRange, TextUnit, SourceFile, AstNode, SyntaxNode, TreeArc, SyntaxNodePtr, | ||
14 | ast, | ||
15 | }; | ||
16 | |||
17 | use crate::{MacroCallId, PersistentHirDatabase}; | ||
18 | |||
19 | // Hard-coded defs for now :-( | ||
20 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
21 | pub enum MacroDef { | ||
22 | Vec, | ||
23 | } | ||
24 | |||
25 | impl MacroDef { | ||
26 | /// Expands macro call, returning the expansion and offset to be used to | ||
27 | /// convert ranges between expansion and original source. | ||
28 | pub fn ast_expand(macro_call: &ast::MacroCall) -> Option<(TextUnit, MacroExpansion)> { | ||
29 | let (def, input) = MacroDef::from_call(macro_call)?; | ||
30 | let exp = def.expand(input)?; | ||
31 | let off = macro_call.token_tree()?.syntax().range().start(); | ||
32 | Some((off, exp)) | ||
33 | } | ||
34 | |||
35 | fn from_call(macro_call: &ast::MacroCall) -> Option<(MacroDef, MacroInput)> { | ||
36 | let def = { | ||
37 | let path = macro_call.path()?; | ||
38 | let name_ref = path.segment()?.name_ref()?; | ||
39 | if name_ref.text() == "vec" { | ||
40 | MacroDef::Vec | ||
41 | } else { | ||
42 | return None; | ||
43 | } | ||
44 | }; | ||
45 | |||
46 | let input = { | ||
47 | let arg = macro_call.token_tree()?.syntax(); | ||
48 | MacroInput { text: arg.text().to_string() } | ||
49 | }; | ||
50 | Some((def, input)) | ||
51 | } | ||
52 | |||
53 | fn expand(self, input: MacroInput) -> Option<MacroExpansion> { | ||
54 | match self { | ||
55 | MacroDef::Vec => self.expand_vec(input), | ||
56 | } | ||
57 | } | ||
58 | fn expand_vec(self, input: MacroInput) -> Option<MacroExpansion> { | ||
59 | let text = format!(r"fn dummy() {{ {}; }}", input.text); | ||
60 | let file = SourceFile::parse(&text); | ||
61 | let array_expr = file.syntax().descendants().find_map(ast::ArrayExpr::cast)?; | ||
62 | let ptr = SyntaxNodePtr::new(array_expr.syntax()); | ||
63 | let src_range = TextRange::offset_len(0.into(), TextUnit::of_str(&input.text)); | ||
64 | let ranges_map = vec![(src_range, array_expr.syntax().range())]; | ||
65 | let res = MacroExpansion { text, ranges_map, ptr }; | ||
66 | Some(res) | ||
67 | } | ||
68 | } | ||
69 | |||
70 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
71 | pub struct MacroInput { | ||
72 | // Should be token trees | ||
73 | pub text: String, | ||
74 | } | ||
75 | |||
76 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
77 | pub struct MacroExpansion { | ||
78 | /// The result of macro expansion. Should be token tree as well. | ||
79 | text: String, | ||
80 | /// Correspondence between ranges in the original source code and ranges in | ||
81 | /// the macro. | ||
82 | ranges_map: Vec<(TextRange, TextRange)>, | ||
83 | /// Implementation detail: internally, a macro is expanded to the whole file, | ||
84 | /// even if it is an expression. This `ptr` selects the actual expansion from | ||
85 | /// the expanded file. | ||
86 | ptr: SyntaxNodePtr, | ||
87 | } | ||
88 | |||
89 | impl MacroExpansion { | ||
90 | // FIXME: does not really make sense, macro expansion is not necessary a | ||
91 | // whole file. See `MacroExpansion::ptr` as well. | ||
92 | pub(crate) fn file(&self) -> TreeArc<SourceFile> { | ||
93 | SourceFile::parse(&self.text) | ||
94 | } | ||
95 | |||
96 | pub fn syntax(&self) -> TreeArc<SyntaxNode> { | ||
97 | self.ptr.to_node(&self.file()).to_owned() | ||
98 | } | ||
99 | /// Maps range in the source code to the range in the expanded code. | ||
100 | pub fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> { | ||
101 | for (s_range, t_range) in self.ranges_map.iter() { | ||
102 | if src_range.is_subrange(&s_range) { | ||
103 | let src_at_zero_range = src_range - src_range.start(); | ||
104 | let src_range_offset = src_range.start() - s_range.start(); | ||
105 | let src_range = src_at_zero_range + src_range_offset + t_range.start(); | ||
106 | return Some(src_range); | ||
107 | } | ||
108 | } | ||
109 | None | ||
110 | } | ||
111 | /// Maps range in the expanded code to the range in the source code. | ||
112 | pub fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> { | ||
113 | for (s_range, t_range) in self.ranges_map.iter() { | ||
114 | if tgt_range.is_subrange(&t_range) { | ||
115 | let tgt_at_zero_range = tgt_range - tgt_range.start(); | ||
116 | let tgt_range_offset = tgt_range.start() - t_range.start(); | ||
117 | let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start(); | ||
118 | return Some(src_range); | ||
119 | } | ||
120 | } | ||
121 | None | ||
122 | } | ||
123 | } | ||
124 | |||
125 | pub(crate) fn expand_macro_invocation( | ||
126 | db: &impl PersistentHirDatabase, | ||
127 | invoc: MacroCallId, | ||
128 | ) -> Option<Arc<MacroExpansion>> { | ||
129 | let loc = invoc.loc(db); | ||
130 | let syntax = db.file_item(loc.source_item_id); | ||
131 | let macro_call = ast::MacroCall::cast(&syntax).unwrap(); | ||
132 | |||
133 | let (def, input) = MacroDef::from_call(macro_call)?; | ||
134 | def.expand(input).map(Arc::new) | ||
135 | } | ||