aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_expand/src
diff options
context:
space:
mode:
authorEdwin Cheng <[email protected]>2020-03-02 06:05:15 +0000
committerEdwin Cheng <[email protected]>2020-03-03 17:21:14 +0000
commit1465cc0c4feb52958d3281f066a663e0a52ed67e (patch)
tree933d006064847260d5ec0d816fef5a381e674f90 /crates/ra_hir_expand/src
parent0d554540730925c074693b43503e65476eadbd65 (diff)
Implement concat macro
Diffstat (limited to 'crates/ra_hir_expand/src')
-rw-r--r--crates/ra_hir_expand/src/builtin_derive.rs24
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs126
-rw-r--r--crates/ra_hir_expand/src/db.rs23
-rw-r--r--crates/ra_hir_expand/src/eager.rs93
-rw-r--r--crates/ra_hir_expand/src/hygiene.rs23
-rw-r--r--crates/ra_hir_expand/src/lib.rs59
-rw-r--r--crates/ra_hir_expand/src/name.rs1
7 files changed, 279 insertions, 70 deletions
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs
index 0b70fb9e1..87224481c 100644
--- a/crates/ra_hir_expand/src/builtin_derive.rs
+++ b/crates/ra_hir_expand/src/builtin_derive.rs
@@ -9,7 +9,7 @@ use ra_syntax::{
9}; 9};
10 10
11use crate::db::AstDatabase; 11use crate::db::AstDatabase;
12use crate::{name, quote, MacroCallId, MacroDefId, MacroDefKind}; 12use crate::{name, quote, LazyMacroId, MacroDefId, MacroDefKind};
13 13
14macro_rules! register_builtin { 14macro_rules! register_builtin {
15 ( $($trait:ident => $expand:ident),* ) => { 15 ( $($trait:ident => $expand:ident),* ) => {
@@ -22,7 +22,7 @@ macro_rules! register_builtin {
22 pub fn expand( 22 pub fn expand(
23 &self, 23 &self,
24 db: &dyn AstDatabase, 24 db: &dyn AstDatabase,
25 id: MacroCallId, 25 id: LazyMacroId,
26 tt: &tt::Subtree, 26 tt: &tt::Subtree,
27 ) -> Result<tt::Subtree, mbe::ExpandError> { 27 ) -> Result<tt::Subtree, mbe::ExpandError> {
28 let expander = match *self { 28 let expander = match *self {
@@ -155,7 +155,7 @@ fn expand_simple_derive(
155 155
156fn copy_expand( 156fn copy_expand(
157 _db: &dyn AstDatabase, 157 _db: &dyn AstDatabase,
158 _id: MacroCallId, 158 _id: LazyMacroId,
159 tt: &tt::Subtree, 159 tt: &tt::Subtree,
160) -> Result<tt::Subtree, mbe::ExpandError> { 160) -> Result<tt::Subtree, mbe::ExpandError> {
161 expand_simple_derive(tt, quote! { std::marker::Copy }) 161 expand_simple_derive(tt, quote! { std::marker::Copy })
@@ -163,7 +163,7 @@ fn copy_expand(
163 163
164fn clone_expand( 164fn clone_expand(
165 _db: &dyn AstDatabase, 165 _db: &dyn AstDatabase,
166 _id: MacroCallId, 166 _id: LazyMacroId,
167 tt: &tt::Subtree, 167 tt: &tt::Subtree,
168) -> Result<tt::Subtree, mbe::ExpandError> { 168) -> Result<tt::Subtree, mbe::ExpandError> {
169 expand_simple_derive(tt, quote! { std::clone::Clone }) 169 expand_simple_derive(tt, quote! { std::clone::Clone })
@@ -171,7 +171,7 @@ fn clone_expand(
171 171
172fn default_expand( 172fn default_expand(
173 _db: &dyn AstDatabase, 173 _db: &dyn AstDatabase,
174 _id: MacroCallId, 174 _id: LazyMacroId,
175 tt: &tt::Subtree, 175 tt: &tt::Subtree,
176) -> Result<tt::Subtree, mbe::ExpandError> { 176) -> Result<tt::Subtree, mbe::ExpandError> {
177 expand_simple_derive(tt, quote! { std::default::Default }) 177 expand_simple_derive(tt, quote! { std::default::Default })
@@ -179,7 +179,7 @@ fn default_expand(
179 179
180fn debug_expand( 180fn debug_expand(
181 _db: &dyn AstDatabase, 181 _db: &dyn AstDatabase,
182 _id: MacroCallId, 182 _id: LazyMacroId,
183 tt: &tt::Subtree, 183 tt: &tt::Subtree,
184) -> Result<tt::Subtree, mbe::ExpandError> { 184) -> Result<tt::Subtree, mbe::ExpandError> {
185 expand_simple_derive(tt, quote! { std::fmt::Debug }) 185 expand_simple_derive(tt, quote! { std::fmt::Debug })
@@ -187,7 +187,7 @@ fn debug_expand(
187 187
188fn hash_expand( 188fn hash_expand(
189 _db: &dyn AstDatabase, 189 _db: &dyn AstDatabase,
190 _id: MacroCallId, 190 _id: LazyMacroId,
191 tt: &tt::Subtree, 191 tt: &tt::Subtree,
192) -> Result<tt::Subtree, mbe::ExpandError> { 192) -> Result<tt::Subtree, mbe::ExpandError> {
193 expand_simple_derive(tt, quote! { std::hash::Hash }) 193 expand_simple_derive(tt, quote! { std::hash::Hash })
@@ -195,7 +195,7 @@ fn hash_expand(
195 195
196fn eq_expand( 196fn eq_expand(
197 _db: &dyn AstDatabase, 197 _db: &dyn AstDatabase,
198 _id: MacroCallId, 198 _id: LazyMacroId,
199 tt: &tt::Subtree, 199 tt: &tt::Subtree,
200) -> Result<tt::Subtree, mbe::ExpandError> { 200) -> Result<tt::Subtree, mbe::ExpandError> {
201 expand_simple_derive(tt, quote! { std::cmp::Eq }) 201 expand_simple_derive(tt, quote! { std::cmp::Eq })
@@ -203,7 +203,7 @@ fn eq_expand(
203 203
204fn partial_eq_expand( 204fn partial_eq_expand(
205 _db: &dyn AstDatabase, 205 _db: &dyn AstDatabase,
206 _id: MacroCallId, 206 _id: LazyMacroId,
207 tt: &tt::Subtree, 207 tt: &tt::Subtree,
208) -> Result<tt::Subtree, mbe::ExpandError> { 208) -> Result<tt::Subtree, mbe::ExpandError> {
209 expand_simple_derive(tt, quote! { std::cmp::PartialEq }) 209 expand_simple_derive(tt, quote! { std::cmp::PartialEq })
@@ -211,7 +211,7 @@ fn partial_eq_expand(
211 211
212fn ord_expand( 212fn ord_expand(
213 _db: &dyn AstDatabase, 213 _db: &dyn AstDatabase,
214 _id: MacroCallId, 214 _id: LazyMacroId,
215 tt: &tt::Subtree, 215 tt: &tt::Subtree,
216) -> Result<tt::Subtree, mbe::ExpandError> { 216) -> Result<tt::Subtree, mbe::ExpandError> {
217 expand_simple_derive(tt, quote! { std::cmp::Ord }) 217 expand_simple_derive(tt, quote! { std::cmp::Ord })
@@ -219,7 +219,7 @@ fn ord_expand(
219 219
220fn partial_ord_expand( 220fn partial_ord_expand(
221 _db: &dyn AstDatabase, 221 _db: &dyn AstDatabase,
222 _id: MacroCallId, 222 _id: LazyMacroId,
223 tt: &tt::Subtree, 223 tt: &tt::Subtree,
224) -> Result<tt::Subtree, mbe::ExpandError> { 224) -> Result<tt::Subtree, mbe::ExpandError> {
225 expand_simple_derive(tt, quote! { std::cmp::PartialOrd }) 225 expand_simple_derive(tt, quote! { std::cmp::PartialOrd })
@@ -228,7 +228,7 @@ fn partial_ord_expand(
228#[cfg(test)] 228#[cfg(test)]
229mod tests { 229mod tests {
230 use super::*; 230 use super::*;
231 use crate::{test_db::TestDB, AstId, MacroCallKind, MacroCallLoc}; 231 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
232 use ra_db::{fixture::WithFixture, SourceDatabase}; 232 use ra_db::{fixture::WithFixture, SourceDatabase};
233 233
234 fn expand_builtin_derive(s: &str, expander: BuiltinDeriveExpander) -> String { 234 fn expand_builtin_derive(s: &str, expander: BuiltinDeriveExpander) -> String {
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 {
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index b695c5b8d..ac8256a84 100644
--- a/crates/ra_hir_expand/src/db.rs
+++ b/crates/ra_hir_expand/src/db.rs
@@ -9,8 +9,9 @@ use ra_prof::profile;
9use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode}; 9use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode};
10 10
11use crate::{ 11use crate::{
12 ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, 12 ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId,
13 LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, 13 HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind,
14 MacroFile,
14}; 15};
15 16
16#[derive(Debug, Clone, Eq, PartialEq)] 17#[derive(Debug, Clone, Eq, PartialEq)]
@@ -24,7 +25,7 @@ impl TokenExpander {
24 pub fn expand( 25 pub fn expand(
25 &self, 26 &self,
26 db: &dyn AstDatabase, 27 db: &dyn AstDatabase,
27 id: MacroCallId, 28 id: LazyMacroId,
28 tt: &tt::Subtree, 29 tt: &tt::Subtree,
29 ) -> Result<tt::Subtree, mbe::ExpandError> { 30 ) -> Result<tt::Subtree, mbe::ExpandError> {
30 match self { 31 match self {
@@ -66,6 +67,9 @@ pub trait AstDatabase: SourceDatabase {
66 fn parse_macro(&self, macro_file: MacroFile) 67 fn parse_macro(&self, macro_file: MacroFile)
67 -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>; 68 -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>;
68 fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>; 69 fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>;
70
71 #[salsa::interned]
72 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
69} 73}
70 74
71pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { 75pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
@@ -101,6 +105,7 @@ pub(crate) fn macro_def(
101 MacroDefKind::BuiltInDerive(expander) => { 105 MacroDefKind::BuiltInDerive(expander) => {
102 Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default()))) 106 Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default())))
103 } 107 }
108 MacroDefKind::BuiltInEager(_expander) => None,
104 } 109 }
105} 110}
106 111
@@ -110,6 +115,10 @@ pub(crate) fn macro_arg(
110) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { 115) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
111 let id = match id { 116 let id = match id {
112 MacroCallId::LazyMacro(id) => id, 117 MacroCallId::LazyMacro(id) => id,
118 MacroCallId::EagerMacro(_id) => {
119 // FIXME: support macro_arg for eager macro
120 return None;
121 }
113 }; 122 };
114 let loc = db.lookup_intern_macro(id); 123 let loc = db.lookup_intern_macro(id);
115 let arg = loc.kind.arg(db)?; 124 let arg = loc.kind.arg(db)?;
@@ -123,13 +132,16 @@ pub(crate) fn macro_expand(
123) -> Result<Arc<tt::Subtree>, String> { 132) -> Result<Arc<tt::Subtree>, String> {
124 let lazy_id = match id { 133 let lazy_id = match id {
125 MacroCallId::LazyMacro(id) => id, 134 MacroCallId::LazyMacro(id) => id,
135 MacroCallId::EagerMacro(id) => {
136 return Ok(db.lookup_intern_eager_expansion(id).subtree);
137 }
126 }; 138 };
127 139
128 let loc = db.lookup_intern_macro(lazy_id); 140 let loc = db.lookup_intern_macro(lazy_id);
129 let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?; 141 let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
130 142
131 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; 143 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
132 let tt = macro_rules.0.expand(db, id, &macro_arg.0).map_err(|err| format!("{:?}", err))?; 144 let tt = macro_rules.0.expand(db, lazy_id, &macro_arg.0).map_err(|err| format!("{:?}", err))?;
133 // Set a hard limit for the expanded tt 145 // Set a hard limit for the expanded tt
134 let count = tt.count(); 146 let count = tt.count();
135 if count > 65536 { 147 if count > 65536 {
@@ -177,6 +189,9 @@ pub(crate) fn parse_macro(
177fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { 189fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
178 let lazy_id = match id { 190 let lazy_id = match id {
179 MacroCallId::LazyMacro(id) => id, 191 MacroCallId::LazyMacro(id) => id,
192 MacroCallId::EagerMacro(id) => {
193 return db.lookup_intern_eager_expansion(id).fragment;
194 }
180 }; 195 };
181 let syn = db.lookup_intern_macro(lazy_id).kind.node(db).value; 196 let syn = db.lookup_intern_macro(lazy_id).kind.node(db).value;
182 197
diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs
new file mode 100644
index 000000000..8caf8497e
--- /dev/null
+++ b/crates/ra_hir_expand/src/eager.rs
@@ -0,0 +1,93 @@
1//! Eager expansion related utils
2
3use crate::{
4 ast::{self, AstNode},
5 db::AstDatabase,
6 EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
7};
8
9use ra_parser::FragmentKind;
10use ra_syntax::{algo::replace_descendants, SyntaxElement, SyntaxNode};
11use std::{collections::HashMap, sync::Arc};
12
13fn to_subtree(node: &SyntaxNode) -> Option<tt::Subtree> {
14 let mut subtree = mbe::syntax_node_to_token_tree(node)?.0;
15 subtree.delimiter = None;
16 Some(subtree)
17}
18
19fn lazy_expand(
20 db: &impl AstDatabase,
21 def: &MacroDefId,
22 macro_call: InFile<ast::MacroCall>,
23) -> Option<InFile<SyntaxNode>> {
24 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
25
26 let id: MacroCallId =
27 def.as_lazy_macro(db, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
28
29 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node))
30}
31
32fn eager_macro_recur(
33 db: &impl AstDatabase,
34 curr: InFile<SyntaxNode>,
35 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
36) -> Option<SyntaxNode> {
37 let mut original = curr.value.clone();
38
39 let children = curr.value.descendants().filter_map(ast::MacroCall::cast);
40 let mut replaces: HashMap<SyntaxElement, SyntaxElement> = HashMap::default();
41
42 // Collect replacement
43 for child in children {
44 let def: MacroDefId = macro_resolver(child.path()?)?;
45 let insert = match def.kind {
46 MacroDefKind::BuiltInEager(_) => {
47 let id: MacroCallId =
48 expand_eager_macro(db, curr.with_value(child.clone()), def, macro_resolver)?
49 .into();
50 db.parse_or_expand(id.as_file())?
51 }
52 MacroDefKind::Declarative
53 | MacroDefKind::BuiltIn(_)
54 | MacroDefKind::BuiltInDerive(_) => {
55 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?;
56 // replace macro inside
57 eager_macro_recur(db, expanded, macro_resolver)?
58 }
59 };
60
61 replaces.insert(child.syntax().clone().into(), insert.into());
62 }
63
64 if !replaces.is_empty() {
65 original = replace_descendants(&original, |n| replaces.get(n).cloned());
66 }
67
68 Some(original)
69}
70
71pub fn expand_eager_macro(
72 db: &impl AstDatabase,
73 macro_call: InFile<ast::MacroCall>,
74 def: MacroDefId,
75 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
76) -> Option<EagerMacroId> {
77 let args = macro_call.value.token_tree()?;
78 let parsed_args = mbe::ast_to_token_tree(&args)?.0;
79 let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0;
80 let result = eager_macro_recur(db, macro_call.with_value(parsed_args.syntax_node()), resolver)?;
81
82 let subtree = to_subtree(&result)?;
83
84 if let MacroDefKind::BuiltInEager(eager) = def.kind {
85 let (subtree, fragment) = eager.expand(&subtree).ok()?;
86 let eager =
87 EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id };
88
89 Some(db.intern_eager_expansion(eager))
90 } else {
91 None
92 }
93}
diff --git a/crates/ra_hir_expand/src/hygiene.rs b/crates/ra_hir_expand/src/hygiene.rs
index ed0e7aa78..cb554ae4b 100644
--- a/crates/ra_hir_expand/src/hygiene.rs
+++ b/crates/ra_hir_expand/src/hygiene.rs
@@ -9,7 +9,7 @@ use ra_syntax::ast;
9use crate::{ 9use crate::{
10 db::AstDatabase, 10 db::AstDatabase,
11 name::{AsName, Name}, 11 name::{AsName, Name},
12 HirFileId, HirFileIdRepr, MacroDefKind, 12 HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind,
13}; 13};
14 14
15#[derive(Debug)] 15#[derive(Debug)]
@@ -22,17 +22,18 @@ impl Hygiene {
22 pub fn new(db: &impl AstDatabase, file_id: HirFileId) -> Hygiene { 22 pub fn new(db: &impl AstDatabase, file_id: HirFileId) -> Hygiene {
23 let def_crate = match file_id.0 { 23 let def_crate = match file_id.0 {
24 HirFileIdRepr::FileId(_) => None, 24 HirFileIdRepr::FileId(_) => None,
25 HirFileIdRepr::MacroFile(macro_file) => { 25 HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
26 let lazy_id = match macro_file.macro_call_id { 26 MacroCallId::LazyMacro(id) => {
27 crate::MacroCallId::LazyMacro(id) => id, 27 let loc = db.lookup_intern_macro(id);
28 }; 28 match loc.def.kind {
29 let loc = db.lookup_intern_macro(lazy_id); 29 MacroDefKind::Declarative => loc.def.krate,
30 match loc.def.kind { 30 MacroDefKind::BuiltIn(_) => None,
31 MacroDefKind::Declarative => loc.def.krate, 31 MacroDefKind::BuiltInDerive(_) => None,
32 MacroDefKind::BuiltIn(_) => None, 32 MacroDefKind::BuiltInEager(_) => None,
33 MacroDefKind::BuiltInDerive(_) => None, 33 }
34 } 34 }
35 } 35 MacroCallId::EagerMacro(_id) => None,
36 },
36 }; 37 };
37 Hygiene { def_crate } 38 Hygiene { def_crate }
38 } 39 }
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index 3a1c6d2b0..3fce73e8a 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -12,6 +12,7 @@ pub mod diagnostics;
12pub mod builtin_derive; 12pub mod builtin_derive;
13pub mod builtin_macro; 13pub mod builtin_macro;
14pub mod quote; 14pub mod quote;
15pub mod eager;
15 16
16use std::hash::Hash; 17use std::hash::Hash;
17use std::sync::Arc; 18use std::sync::Arc;
@@ -25,7 +26,7 @@ use ra_syntax::{
25 26
26use crate::ast_id_map::FileAstId; 27use crate::ast_id_map::FileAstId;
27use crate::builtin_derive::BuiltinDeriveExpander; 28use crate::builtin_derive::BuiltinDeriveExpander;
28use crate::builtin_macro::BuiltinFnLikeExpander; 29use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander};
29 30
30#[cfg(test)] 31#[cfg(test)]
31mod test_db; 32mod test_db;
@@ -70,11 +71,17 @@ impl HirFileId {
70 match self.0 { 71 match self.0 {
71 HirFileIdRepr::FileId(file_id) => file_id, 72 HirFileIdRepr::FileId(file_id) => file_id,
72 HirFileIdRepr::MacroFile(macro_file) => { 73 HirFileIdRepr::MacroFile(macro_file) => {
73 let lazy_id = match macro_file.macro_call_id { 74 let file_id = match macro_file.macro_call_id {
74 MacroCallId::LazyMacro(id) => id, 75 MacroCallId::LazyMacro(id) => {
76 let loc = db.lookup_intern_macro(id);
77 loc.kind.file_id()
78 }
79 MacroCallId::EagerMacro(id) => {
80 let loc = db.lookup_intern_eager_expansion(id);
81 loc.file_id
82 }
75 }; 83 };
76 let loc = db.lookup_intern_macro(lazy_id); 84 file_id.original_file(db)
77 loc.kind.file_id().original_file(db)
78 } 85 }
79 } 86 }
80 } 87 }
@@ -86,6 +93,10 @@ impl HirFileId {
86 HirFileIdRepr::MacroFile(macro_file) => { 93 HirFileIdRepr::MacroFile(macro_file) => {
87 let lazy_id = match macro_file.macro_call_id { 94 let lazy_id = match macro_file.macro_call_id {
88 MacroCallId::LazyMacro(id) => id, 95 MacroCallId::LazyMacro(id) => id,
96 MacroCallId::EagerMacro(_id) => {
97 // FIXME: handle call node for eager macro
98 return None;
99 }
89 }; 100 };
90 let loc = db.lookup_intern_macro(lazy_id); 101 let loc = db.lookup_intern_macro(lazy_id);
91 Some(loc.kind.node(db)) 102 Some(loc.kind.node(db))
@@ -100,6 +111,10 @@ impl HirFileId {
100 HirFileIdRepr::MacroFile(macro_file) => { 111 HirFileIdRepr::MacroFile(macro_file) => {
101 let lazy_id = match macro_file.macro_call_id { 112 let lazy_id = match macro_file.macro_call_id {
102 MacroCallId::LazyMacro(id) => id, 113 MacroCallId::LazyMacro(id) => id,
114 MacroCallId::EagerMacro(_id) => {
115 // FIXME: handle expansion_info for eager macro
116 return None;
117 }
103 }; 118 };
104 let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id); 119 let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id);
105 120
@@ -129,6 +144,9 @@ impl HirFileId {
129 HirFileIdRepr::MacroFile(macro_file) => { 144 HirFileIdRepr::MacroFile(macro_file) => {
130 let lazy_id = match macro_file.macro_call_id { 145 let lazy_id = match macro_file.macro_call_id {
131 MacroCallId::LazyMacro(id) => id, 146 MacroCallId::LazyMacro(id) => id,
147 MacroCallId::EagerMacro(_id) => {
148 return None;
149 }
132 }; 150 };
133 let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id); 151 let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id);
134 let item = match loc.def.kind { 152 let item = match loc.def.kind {
@@ -151,6 +169,7 @@ pub struct MacroFile {
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 169#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
152pub enum MacroCallId { 170pub enum MacroCallId {
153 LazyMacro(LazyMacroId), 171 LazyMacro(LazyMacroId),
172 EagerMacro(EagerMacroId),
154} 173}
155 174
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 175#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -164,11 +183,27 @@ impl salsa::InternKey for LazyMacroId {
164 } 183 }
165} 184}
166 185
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
187pub struct EagerMacroId(salsa::InternId);
188impl salsa::InternKey for EagerMacroId {
189 fn from_intern_id(v: salsa::InternId) -> Self {
190 EagerMacroId(v)
191 }
192 fn as_intern_id(&self) -> salsa::InternId {
193 self.0
194 }
195}
196
167impl From<LazyMacroId> for MacroCallId { 197impl From<LazyMacroId> for MacroCallId {
168 fn from(it: LazyMacroId) -> Self { 198 fn from(it: LazyMacroId) -> Self {
169 MacroCallId::LazyMacro(it) 199 MacroCallId::LazyMacro(it)
170 } 200 }
171} 201}
202impl From<EagerMacroId> for MacroCallId {
203 fn from(it: EagerMacroId) -> Self {
204 MacroCallId::EagerMacro(it)
205 }
206}
172 207
173#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 208#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
174pub struct MacroDefId { 209pub struct MacroDefId {
@@ -184,8 +219,8 @@ pub struct MacroDefId {
184} 219}
185 220
186impl MacroDefId { 221impl MacroDefId {
187 pub fn as_call_id(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> MacroCallId { 222 pub fn as_lazy_macro(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> LazyMacroId {
188 db.intern_macro(MacroCallLoc { def: self, kind }).into() 223 db.intern_macro(MacroCallLoc { def: self, kind })
189 } 224 }
190} 225}
191 226
@@ -195,6 +230,7 @@ pub enum MacroDefKind {
195 BuiltIn(BuiltinFnLikeExpander), 230 BuiltIn(BuiltinFnLikeExpander),
196 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander 231 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander
197 BuiltInDerive(BuiltinDeriveExpander), 232 BuiltInDerive(BuiltinDeriveExpander),
233 BuiltInEager(EagerExpander),
198} 234}
199 235
200#[derive(Debug, Clone, PartialEq, Eq, Hash)] 236#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -240,6 +276,14 @@ impl MacroCallId {
240 } 276 }
241} 277}
242 278
279#[derive(Debug, Clone, PartialEq, Eq, Hash)]
280pub struct EagerCallLoc {
281 pub(crate) def: MacroDefId,
282 pub(crate) fragment: FragmentKind,
283 pub(crate) subtree: Arc<tt::Subtree>,
284 pub(crate) file_id: HirFileId,
285}
286
243/// ExpansionInfo mainly describes how to map text range between src and expanded macro 287/// ExpansionInfo mainly describes how to map text range between src and expanded macro
244#[derive(Debug, Clone, PartialEq, Eq)] 288#[derive(Debug, Clone, PartialEq, Eq)]
245pub struct ExpansionInfo { 289pub struct ExpansionInfo {
@@ -253,6 +297,7 @@ pub struct ExpansionInfo {
253} 297}
254 298
255pub use mbe::Origin; 299pub use mbe::Origin;
300use ra_parser::FragmentKind;
256 301
257impl ExpansionInfo { 302impl ExpansionInfo {
258 pub fn call_node(&self) -> Option<InFile<SyntaxNode>> { 303 pub fn call_node(&self) -> Option<InFile<SyntaxNode>> {
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index b2e10f445..036cf7d1e 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -173,6 +173,7 @@ pub mod known {
173 compile_error, 173 compile_error,
174 line, 174 line,
175 stringify, 175 stringify,
176 concat,
176 format_args, 177 format_args,
177 format_args_nl, 178 format_args_nl,
178 env, 179 env,