diff options
Diffstat (limited to 'crates/ra_hir_expand/src/builtin_macro.rs')
-rw-r--r-- | crates/ra_hir_expand/src/builtin_macro.rs | 232 |
1 files changed, 207 insertions, 25 deletions
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index 97fb0cb55..c0e0436c0 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs | |||
@@ -8,35 +8,47 @@ use crate::{ | |||
8 | 8 | ||
9 | use crate::quote; | 9 | use crate::quote; |
10 | 10 | ||
11 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 11 | macro_rules! register_builtin { |
12 | pub enum BuiltinExpander { | 12 | ( $(($name:ident, $kind: ident) => $expand:ident),* ) => { |
13 | Line, | 13 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
14 | } | 14 | pub enum BuiltinFnLikeExpander { |
15 | $($kind),* | ||
16 | } | ||
15 | 17 | ||
16 | impl BuiltinExpander { | 18 | impl BuiltinFnLikeExpander { |
17 | pub fn expand( | 19 | pub fn expand( |
18 | &self, | 20 | &self, |
19 | db: &dyn AstDatabase, | 21 | db: &dyn AstDatabase, |
20 | id: MacroCallId, | 22 | id: MacroCallId, |
21 | tt: &tt::Subtree, | 23 | tt: &tt::Subtree, |
22 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 24 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
23 | match self { | 25 | let expander = match *self { |
24 | BuiltinExpander::Line => line_expand(db, id, tt), | 26 | $( BuiltinFnLikeExpander::$kind => $expand, )* |
27 | }; | ||
28 | expander(db, id, tt) | ||
29 | } | ||
25 | } | 30 | } |
26 | } | 31 | |
32 | pub fn find_builtin_macro( | ||
33 | ident: &name::Name, | ||
34 | krate: CrateId, | ||
35 | ast_id: AstId<ast::MacroCall>, | ||
36 | ) -> Option<MacroDefId> { | ||
37 | let kind = match ident { | ||
38 | $( id if id == &name::$name => BuiltinFnLikeExpander::$kind, )* | ||
39 | _ => return None, | ||
40 | }; | ||
41 | |||
42 | Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(kind) }) | ||
43 | } | ||
44 | }; | ||
27 | } | 45 | } |
28 | 46 | ||
29 | pub fn find_builtin_macro( | 47 | register_builtin! { |
30 | ident: &name::Name, | 48 | (COLUMN_MACRO, Column) => column_expand, |
31 | krate: CrateId, | 49 | (FILE_MACRO, File) => file_expand, |
32 | ast_id: AstId<ast::MacroCall>, | 50 | (LINE_MACRO, Line) => line_expand, |
33 | ) -> Option<MacroDefId> { | 51 | (STRINGIFY_MACRO, Stringify) => stringify_expand |
34 | // FIXME: Better registering method | ||
35 | if ident == &name::LINE_MACRO { | ||
36 | Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Line) }) | ||
37 | } else { | ||
38 | None | ||
39 | } | ||
40 | } | 52 | } |
41 | 53 | ||
42 | fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { | 54 | fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { |
@@ -78,3 +90,173 @@ fn line_expand( | |||
78 | 90 | ||
79 | Ok(expanded) | 91 | Ok(expanded) |
80 | } | 92 | } |
93 | |||
94 | fn stringify_expand( | ||
95 | db: &dyn AstDatabase, | ||
96 | id: MacroCallId, | ||
97 | _tt: &tt::Subtree, | ||
98 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
99 | let loc = db.lookup_intern_macro(id); | ||
100 | let macro_call = loc.ast_id.to_node(db); | ||
101 | |||
102 | let macro_content = { | ||
103 | let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; | ||
104 | let macro_args = arg.syntax().clone(); | ||
105 | let text = macro_args.text(); | ||
106 | let without_parens = TextUnit::of_char('(')..text.len() - TextUnit::of_char(')'); | ||
107 | text.slice(without_parens).to_string() | ||
108 | }; | ||
109 | |||
110 | let expanded = quote! { | ||
111 | #macro_content | ||
112 | }; | ||
113 | |||
114 | Ok(expanded) | ||
115 | } | ||
116 | |||
117 | fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { | ||
118 | // FIXME: Use expansion info | ||
119 | let file_id = file.original_file(db); | ||
120 | let text = db.file_text(file_id); | ||
121 | let mut col_num = 1; | ||
122 | |||
123 | for c in text[..pos.to_usize()].chars().rev() { | ||
124 | if c == '\n' { | ||
125 | break; | ||
126 | } | ||
127 | col_num = col_num + 1; | ||
128 | } | ||
129 | |||
130 | col_num | ||
131 | } | ||
132 | |||
133 | fn column_expand( | ||
134 | db: &dyn AstDatabase, | ||
135 | id: MacroCallId, | ||
136 | _tt: &tt::Subtree, | ||
137 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
138 | let loc = db.lookup_intern_macro(id); | ||
139 | let macro_call = loc.ast_id.to_node(db); | ||
140 | |||
141 | let _arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; | ||
142 | let col_start = macro_call.syntax().text_range().start(); | ||
143 | |||
144 | let file = id.as_file(MacroFileKind::Expr); | ||
145 | let col_num = to_col_number(db, file, col_start); | ||
146 | |||
147 | let expanded = quote! { | ||
148 | #col_num | ||
149 | }; | ||
150 | |||
151 | Ok(expanded) | ||
152 | } | ||
153 | |||
154 | fn file_expand( | ||
155 | db: &dyn AstDatabase, | ||
156 | id: MacroCallId, | ||
157 | _tt: &tt::Subtree, | ||
158 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
159 | let loc = db.lookup_intern_macro(id); | ||
160 | let macro_call = loc.ast_id.to_node(db); | ||
161 | |||
162 | let _ = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?; | ||
163 | |||
164 | // FIXME: RA purposefully lacks knowledge of absolute file names | ||
165 | // so just return "". | ||
166 | let file_name = ""; | ||
167 | |||
168 | let expanded = quote! { | ||
169 | #file_name | ||
170 | }; | ||
171 | |||
172 | Ok(expanded) | ||
173 | } | ||
174 | |||
175 | #[cfg(test)] | ||
176 | mod tests { | ||
177 | use super::*; | ||
178 | use crate::{test_db::TestDB, MacroCallLoc}; | ||
179 | use ra_db::{fixture::WithFixture, SourceDatabase}; | ||
180 | |||
181 | fn expand_builtin_macro(s: &str, expander: BuiltinFnLikeExpander) -> String { | ||
182 | let (db, file_id) = TestDB::with_single_file(&s); | ||
183 | let parsed = db.parse(file_id); | ||
184 | let macro_calls: Vec<_> = | ||
185 | parsed.syntax_node().descendants().filter_map(|it| ast::MacroCall::cast(it)).collect(); | ||
186 | |||
187 | let ast_id_map = db.ast_id_map(file_id.into()); | ||
188 | |||
189 | // the first one should be a macro_rules | ||
190 | let def = MacroDefId { | ||
191 | krate: CrateId(0), | ||
192 | ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0])), | ||
193 | kind: MacroDefKind::BuiltIn(expander), | ||
194 | }; | ||
195 | |||
196 | let loc = MacroCallLoc { | ||
197 | def, | ||
198 | ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[1])), | ||
199 | }; | ||
200 | |||
201 | let id = db.intern_macro(loc); | ||
202 | let parsed = db.parse_or_expand(id.as_file(MacroFileKind::Expr)).unwrap(); | ||
203 | |||
204 | parsed.text().to_string() | ||
205 | } | ||
206 | |||
207 | #[test] | ||
208 | fn test_column_expand() { | ||
209 | let expanded = expand_builtin_macro( | ||
210 | r#" | ||
211 | #[rustc_builtin_macro] | ||
212 | macro_rules! column {() => {}} | ||
213 | column!() | ||
214 | "#, | ||
215 | BuiltinFnLikeExpander::Column, | ||
216 | ); | ||
217 | |||
218 | assert_eq!(expanded, "9"); | ||
219 | } | ||
220 | |||
221 | #[test] | ||
222 | fn test_line_expand() { | ||
223 | let expanded = expand_builtin_macro( | ||
224 | r#" | ||
225 | #[rustc_builtin_macro] | ||
226 | macro_rules! line {() => {}} | ||
227 | line!() | ||
228 | "#, | ||
229 | BuiltinFnLikeExpander::Line, | ||
230 | ); | ||
231 | |||
232 | assert_eq!(expanded, "4"); | ||
233 | } | ||
234 | |||
235 | #[test] | ||
236 | fn test_stringify_expand() { | ||
237 | let expanded = expand_builtin_macro( | ||
238 | r#" | ||
239 | #[rustc_builtin_macro] | ||
240 | macro_rules! stringify {() => {}} | ||
241 | stringify!(a b c) | ||
242 | "#, | ||
243 | BuiltinFnLikeExpander::Stringify, | ||
244 | ); | ||
245 | |||
246 | assert_eq!(expanded, "\"a b c\""); | ||
247 | } | ||
248 | |||
249 | #[test] | ||
250 | fn test_file_expand() { | ||
251 | let expanded = expand_builtin_macro( | ||
252 | r#" | ||
253 | #[rustc_builtin_macro] | ||
254 | macro_rules! file {() => {}} | ||
255 | file!() | ||
256 | "#, | ||
257 | BuiltinFnLikeExpander::File, | ||
258 | ); | ||
259 | |||
260 | assert_eq!(expanded, "\"\""); | ||
261 | } | ||
262 | } | ||