aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/macros.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/macros.rs')
-rw-r--r--crates/ra_hir/src/macros.rs135
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!
10use std::sync::Arc;
11
12use ra_syntax::{
13 TextRange, TextUnit, SourceFile, AstNode, SyntaxNode, TreeArc, SyntaxNodePtr,
14 ast,
15};
16
17use crate::{MacroCallId, PersistentHirDatabase};
18
19// Hard-coded defs for now :-(
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum MacroDef {
22 Vec,
23}
24
25impl 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)]
71pub struct MacroInput {
72 // Should be token trees
73 pub text: String,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
77pub 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
89impl 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
125pub(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}