diff options
author | Edwin Cheng <[email protected]> | 2020-03-02 06:05:15 +0000 |
---|---|---|
committer | Edwin Cheng <[email protected]> | 2020-03-03 17:21:14 +0000 |
commit | 1465cc0c4feb52958d3281f066a663e0a52ed67e (patch) | |
tree | 933d006064847260d5ec0d816fef5a381e674f90 /crates/ra_hir_expand/src/builtin_macro.rs | |
parent | 0d554540730925c074693b43503e65476eadbd65 (diff) |
Implement concat macro
Diffstat (limited to 'crates/ra_hir_expand/src/builtin_macro.rs')
-rw-r--r-- | crates/ra_hir_expand/src/builtin_macro.rs | 126 |
1 files changed, 90 insertions, 36 deletions
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index d91aa4ffa..1f380b571 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs | |||
@@ -1,24 +1,31 @@ | |||
1 | //! Builtin macro | 1 | //! Builtin macro |
2 | use crate::db::AstDatabase; | 2 | use crate::db::AstDatabase; |
3 | use crate::{ | 3 | use crate::{ |
4 | ast::{self}, | 4 | ast::{self, AstToken, HasStringValue}, |
5 | name, AstId, CrateId, MacroCallId, MacroDefId, MacroDefKind, TextUnit, | 5 | name, AstId, CrateId, MacroDefId, MacroDefKind, TextUnit, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use crate::quote; | 8 | use crate::{quote, LazyMacroId}; |
9 | use either::Either; | ||
10 | use ra_parser::FragmentKind; | ||
9 | 11 | ||
10 | macro_rules! register_builtin { | 12 | macro_rules! register_builtin { |
11 | ( $(($name:ident, $kind: ident) => $expand:ident),* ) => { | 13 | ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => { |
12 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 14 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
13 | pub enum BuiltinFnLikeExpander { | 15 | pub enum BuiltinFnLikeExpander { |
14 | $($kind),* | 16 | $($kind),* |
15 | } | 17 | } |
16 | 18 | ||
19 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
20 | pub enum EagerExpander { | ||
21 | $($e_kind),* | ||
22 | } | ||
23 | |||
17 | impl BuiltinFnLikeExpander { | 24 | impl BuiltinFnLikeExpander { |
18 | pub fn expand( | 25 | pub fn expand( |
19 | &self, | 26 | &self, |
20 | db: &dyn AstDatabase, | 27 | db: &dyn AstDatabase, |
21 | id: MacroCallId, | 28 | id: LazyMacroId, |
22 | tt: &tt::Subtree, | 29 | tt: &tt::Subtree, |
23 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 30 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
24 | let expander = match *self { | 31 | let expander = match *self { |
@@ -26,28 +33,54 @@ macro_rules! register_builtin { | |||
26 | }; | 33 | }; |
27 | expander(db, id, tt) | 34 | expander(db, id, tt) |
28 | } | 35 | } |
36 | } | ||
29 | 37 | ||
30 | fn by_name(ident: &name::Name) -> Option<BuiltinFnLikeExpander> { | 38 | impl EagerExpander { |
31 | match ident { | 39 | pub fn expand( |
32 | $( id if id == &name::name![$name] => Some(BuiltinFnLikeExpander::$kind), )* | 40 | &self, |
33 | _ => return None, | 41 | tt: &tt::Subtree, |
34 | } | 42 | ) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { |
43 | let expander = match *self { | ||
44 | $( EagerExpander::$e_kind => $e_expand, )* | ||
45 | }; | ||
46 | expander(tt) | ||
35 | } | 47 | } |
36 | } | 48 | } |
37 | 49 | ||
38 | pub fn find_builtin_macro( | 50 | fn find_by_name(ident: &name::Name) -> Option<Either<BuiltinFnLikeExpander, EagerExpander>> { |
39 | ident: &name::Name, | 51 | match ident { |
40 | krate: CrateId, | 52 | $( id if id == &name::name![$name] => Some(Either::Left(BuiltinFnLikeExpander::$kind)), )* |
41 | ast_id: AstId<ast::MacroCall>, | 53 | $( id if id == &name::name![$e_name] => Some(Either::Right(EagerExpander::$e_kind)), )* |
42 | ) -> Option<MacroDefId> { | 54 | _ => return None, |
43 | let kind = BuiltinFnLikeExpander::by_name(ident)?; | 55 | } |
44 | |||
45 | Some(MacroDefId { krate: Some(krate), ast_id: Some(ast_id), kind: MacroDefKind::BuiltIn(kind) }) | ||
46 | } | 56 | } |
47 | }; | 57 | }; |
48 | } | 58 | } |
49 | 59 | ||
60 | pub fn find_builtin_macro( | ||
61 | ident: &name::Name, | ||
62 | krate: CrateId, | ||
63 | ast_id: AstId<ast::MacroCall>, | ||
64 | ) -> Option<MacroDefId> { | ||
65 | let kind = find_by_name(ident)?; | ||
66 | |||
67 | match kind { | ||
68 | Either::Left(kind) => Some(MacroDefId { | ||
69 | krate: Some(krate), | ||
70 | ast_id: Some(ast_id), | ||
71 | kind: MacroDefKind::BuiltIn(kind), | ||
72 | }), | ||
73 | Either::Right(kind) => Some(MacroDefId { | ||
74 | krate: Some(krate), | ||
75 | ast_id: Some(ast_id), | ||
76 | kind: MacroDefKind::BuiltInEager(kind), | ||
77 | }), | ||
78 | } | ||
79 | } | ||
80 | |||
50 | register_builtin! { | 81 | register_builtin! { |
82 | LAZY: | ||
83 | |||
51 | (column, Column) => column_expand, | 84 | (column, Column) => column_expand, |
52 | (compile_error, CompileError) => compile_error_expand, | 85 | (compile_error, CompileError) => compile_error_expand, |
53 | (file, File) => file_expand, | 86 | (file, File) => file_expand, |
@@ -58,12 +91,16 @@ register_builtin! { | |||
58 | (option_env, OptionEnv) => option_env_expand, | 91 | (option_env, OptionEnv) => option_env_expand, |
59 | // format_args_nl only differs in that it adds a newline in the end, | 92 | // format_args_nl only differs in that it adds a newline in the end, |
60 | // so we use the same stub expansion for now | 93 | // so we use the same stub expansion for now |
61 | (format_args_nl, FormatArgsNl) => format_args_expand | 94 | (format_args_nl, FormatArgsNl) => format_args_expand, |
95 | |||
96 | EAGER: | ||
97 | // eagers | ||
98 | (concat, Concat) => concat_expand | ||
62 | } | 99 | } |
63 | 100 | ||
64 | fn line_expand( | 101 | fn line_expand( |
65 | _db: &dyn AstDatabase, | 102 | _db: &dyn AstDatabase, |
66 | _id: MacroCallId, | 103 | _id: LazyMacroId, |
67 | _tt: &tt::Subtree, | 104 | _tt: &tt::Subtree, |
68 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 105 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
69 | // dummy implementation for type-checking purposes | 106 | // dummy implementation for type-checking purposes |
@@ -77,13 +114,9 @@ fn line_expand( | |||
77 | 114 | ||
78 | fn stringify_expand( | 115 | fn stringify_expand( |
79 | db: &dyn AstDatabase, | 116 | db: &dyn AstDatabase, |
80 | id: MacroCallId, | 117 | id: LazyMacroId, |
81 | _tt: &tt::Subtree, | 118 | _tt: &tt::Subtree, |
82 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 119 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
83 | let id = match id { | ||
84 | MacroCallId::LazyMacro(id) => id, | ||
85 | }; | ||
86 | |||
87 | let loc = db.lookup_intern_macro(id); | 120 | let loc = db.lookup_intern_macro(id); |
88 | 121 | ||
89 | let macro_content = { | 122 | let macro_content = { |
@@ -103,7 +136,7 @@ fn stringify_expand( | |||
103 | 136 | ||
104 | fn env_expand( | 137 | fn env_expand( |
105 | _db: &dyn AstDatabase, | 138 | _db: &dyn AstDatabase, |
106 | _id: MacroCallId, | 139 | _id: LazyMacroId, |
107 | _tt: &tt::Subtree, | 140 | _tt: &tt::Subtree, |
108 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 141 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
109 | // dummy implementation for type-checking purposes | 142 | // dummy implementation for type-checking purposes |
@@ -114,7 +147,7 @@ fn env_expand( | |||
114 | 147 | ||
115 | fn option_env_expand( | 148 | fn option_env_expand( |
116 | _db: &dyn AstDatabase, | 149 | _db: &dyn AstDatabase, |
117 | _id: MacroCallId, | 150 | _id: LazyMacroId, |
118 | _tt: &tt::Subtree, | 151 | _tt: &tt::Subtree, |
119 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 152 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
120 | // dummy implementation for type-checking purposes | 153 | // dummy implementation for type-checking purposes |
@@ -125,7 +158,7 @@ fn option_env_expand( | |||
125 | 158 | ||
126 | fn column_expand( | 159 | fn column_expand( |
127 | _db: &dyn AstDatabase, | 160 | _db: &dyn AstDatabase, |
128 | _id: MacroCallId, | 161 | _id: LazyMacroId, |
129 | _tt: &tt::Subtree, | 162 | _tt: &tt::Subtree, |
130 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 163 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
131 | // dummy implementation for type-checking purposes | 164 | // dummy implementation for type-checking purposes |
@@ -139,7 +172,7 @@ fn column_expand( | |||
139 | 172 | ||
140 | fn file_expand( | 173 | fn file_expand( |
141 | _db: &dyn AstDatabase, | 174 | _db: &dyn AstDatabase, |
142 | _id: MacroCallId, | 175 | _id: LazyMacroId, |
143 | _tt: &tt::Subtree, | 176 | _tt: &tt::Subtree, |
144 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 177 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
145 | // FIXME: RA purposefully lacks knowledge of absolute file names | 178 | // FIXME: RA purposefully lacks knowledge of absolute file names |
@@ -155,7 +188,7 @@ fn file_expand( | |||
155 | 188 | ||
156 | fn compile_error_expand( | 189 | fn compile_error_expand( |
157 | _db: &dyn AstDatabase, | 190 | _db: &dyn AstDatabase, |
158 | _id: MacroCallId, | 191 | _id: LazyMacroId, |
159 | tt: &tt::Subtree, | 192 | tt: &tt::Subtree, |
160 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 193 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
161 | if tt.count() == 1 { | 194 | if tt.count() == 1 { |
@@ -172,7 +205,7 @@ fn compile_error_expand( | |||
172 | 205 | ||
173 | fn format_args_expand( | 206 | fn format_args_expand( |
174 | _db: &dyn AstDatabase, | 207 | _db: &dyn AstDatabase, |
175 | _id: MacroCallId, | 208 | _id: LazyMacroId, |
176 | tt: &tt::Subtree, | 209 | tt: &tt::Subtree, |
177 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 210 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
178 | // We expand `format_args!("", a1, a2)` to | 211 | // We expand `format_args!("", a1, a2)` to |
@@ -212,23 +245,44 @@ fn format_args_expand( | |||
212 | Ok(expanded) | 245 | Ok(expanded) |
213 | } | 246 | } |
214 | 247 | ||
248 | fn unquote_str(lit: &tt::Literal) -> Option<String> { | ||
249 | let lit = ast::make::tokens::literal(&lit.to_string()); | ||
250 | let token = ast::String::cast(lit)?; | ||
251 | token.value() | ||
252 | } | ||
253 | |||
254 | fn concat_expand(tt: &tt::Subtree) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { | ||
255 | let mut text = String::new(); | ||
256 | for (i, t) in tt.token_trees.iter().enumerate() { | ||
257 | match t { | ||
258 | tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => { | ||
259 | text += &unquote_str(&it).ok_or_else(|| mbe::ExpandError::ConversionError)?; | ||
260 | } | ||
261 | tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), | ||
262 | _ => return Err(mbe::ExpandError::UnexpectedToken), | ||
263 | } | ||
264 | } | ||
265 | |||
266 | Ok((quote!(#text), FragmentKind::Expr)) | ||
267 | } | ||
268 | |||
215 | #[cfg(test)] | 269 | #[cfg(test)] |
216 | mod tests { | 270 | mod tests { |
217 | use super::*; | 271 | use super::*; |
218 | use crate::{name::AsName, test_db::TestDB, AstNode, MacroCallKind, MacroCallLoc}; | 272 | use crate::{name::AsName, test_db::TestDB, AstNode, MacroCallId, MacroCallKind, MacroCallLoc}; |
219 | use ra_db::{fixture::WithFixture, SourceDatabase}; | 273 | use ra_db::{fixture::WithFixture, SourceDatabase}; |
220 | use ra_syntax::ast::NameOwner; | 274 | use ra_syntax::ast::NameOwner; |
221 | 275 | ||
222 | fn expand_builtin_macro(s: &str) -> String { | 276 | fn expand_builtin_macro(ra_fixture: &str) -> String { |
223 | let (db, file_id) = TestDB::with_single_file(&s); | 277 | let (db, file_id) = TestDB::with_single_file(&ra_fixture); |
224 | let parsed = db.parse(file_id); | 278 | let parsed = db.parse(file_id); |
225 | let macro_calls: Vec<_> = | 279 | let macro_calls: Vec<_> = |
226 | parsed.syntax_node().descendants().filter_map(ast::MacroCall::cast).collect(); | 280 | parsed.syntax_node().descendants().filter_map(ast::MacroCall::cast).collect(); |
227 | 281 | ||
228 | let ast_id_map = db.ast_id_map(file_id.into()); | 282 | let ast_id_map = db.ast_id_map(file_id.into()); |
229 | 283 | ||
230 | let expander = | 284 | let expander = find_by_name(¯o_calls[0].name().unwrap().as_name()).unwrap(); |
231 | BuiltinFnLikeExpander::by_name(¯o_calls[0].name().unwrap().as_name()).unwrap(); | 285 | let expander = expander.left().unwrap(); |
232 | 286 | ||
233 | // the first one should be a macro_rules | 287 | // the first one should be a macro_rules |
234 | let def = MacroDefId { | 288 | let def = MacroDefId { |