aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_expand/src/builtin_macro.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_expand/src/builtin_macro.rs')
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs126
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
2use crate::db::AstDatabase; 2use crate::db::AstDatabase;
3use crate::{ 3use 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
8use crate::quote; 8use crate::{quote, LazyMacroId};
9use either::Either;
10use ra_parser::FragmentKind;
9 11
10macro_rules! register_builtin { 12macro_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
60pub 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
50register_builtin! { 81register_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
64fn line_expand( 101fn 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
78fn stringify_expand( 115fn 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
104fn env_expand( 137fn 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
115fn option_env_expand( 148fn 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
126fn column_expand( 159fn 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
140fn file_expand( 173fn 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
156fn compile_error_expand( 189fn 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
173fn format_args_expand( 206fn 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
248fn 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
254fn 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)]
216mod tests { 270mod 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(&macro_calls[0].name().unwrap().as_name()).unwrap();
231 BuiltinFnLikeExpander::by_name(&macro_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 {