aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-03-03 17:30:08 +0000
committerGitHub <[email protected]>2020-03-03 17:30:08 +0000
commit7a322f9afff05b88507a6956a2d84a3abef0a0d6 (patch)
treef811a7f405edca2b7e0c32666604117ef6486229
parent13b25d73b56ede36d1680efc19f5c11b0669b96c (diff)
parent4d5e80c6c86aa6bfee50e9f8b80b365c3120ed80 (diff)
Merge #3392
3392: Implement concat eager macro r=matklad a=edwin0cheng This PR implements the following things: 1. Add basic eager macro infrastructure by introducing `EagerCallId` such that the new `MacroCallId` is defined as : ``` #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum MacroCallId { LazyMacro(LazyMacroId), EagerMacro(EagerMacroId), } ``` 2. Add `concat!` builtin macro. Co-authored-by: Edwin Cheng <[email protected]>
-rw-r--r--crates/ra_hir_def/src/lib.rs23
-rw-r--r--crates/ra_hir_expand/src/builtin_derive.rs26
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs124
-rw-r--r--crates/ra_hir_expand/src/db.rs58
-rw-r--r--crates/ra_hir_expand/src/eager.rs112
-rw-r--r--crates/ra_hir_expand/src/hygiene.rs20
-rw-r--r--crates/ra_hir_expand/src/lib.rs88
-rw-r--r--crates/ra_hir_expand/src/name.rs1
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs19
-rw-r--r--crates/ra_parser/src/lib.rs2
-rw-r--r--crates/ra_syntax/src/ast/make.rs8
11 files changed, 399 insertions, 82 deletions
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index c9b14d0c8..a3d617e1f 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -47,8 +47,8 @@ mod marks;
47use std::hash::Hash; 47use std::hash::Hash;
48 48
49use hir_expand::{ 49use hir_expand::{
50 ast_id_map::FileAstId, db::AstDatabase, hygiene::Hygiene, AstId, HirFileId, InFile, 50 ast_id_map::FileAstId, db::AstDatabase, eager::expand_eager_macro, hygiene::Hygiene, AstId,
51 MacroCallId, MacroCallKind, MacroDefId, 51 HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
52}; 52};
53use ra_arena::{impl_arena_id, RawId}; 53use ra_arena::{impl_arena_id, RawId};
54use ra_db::{impl_intern_key, salsa, CrateId}; 54use ra_db::{impl_intern_key, salsa, CrateId};
@@ -459,8 +459,21 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
459 db: &impl AstDatabase, 459 db: &impl AstDatabase,
460 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 460 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
461 ) -> Option<MacroCallId> { 461 ) -> Option<MacroCallId> {
462 let def = resolver(self.path.clone())?; 462 let def: MacroDefId = resolver(self.path.clone())?;
463 Some(def.as_call_id(db, MacroCallKind::FnLike(self.ast_id))) 463
464 if let MacroDefKind::BuiltInEager(_) = def.kind {
465 let macro_call = InFile::new(self.ast_id.file_id, self.ast_id.to_node(db));
466 let hygiene = Hygiene::new(db, self.ast_id.file_id);
467
468 Some(
469 expand_eager_macro(db, macro_call, def, &|path: ast::Path| {
470 resolver(path::ModPath::from_src(path, &hygiene)?)
471 })?
472 .into(),
473 )
474 } else {
475 Some(def.as_lazy_macro(db, MacroCallKind::FnLike(self.ast_id)).into())
476 }
464 } 477 }
465} 478}
466 479
@@ -471,6 +484,6 @@ impl AsMacroCall for AstIdWithPath<ast::ModuleItem> {
471 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 484 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
472 ) -> Option<MacroCallId> { 485 ) -> Option<MacroCallId> {
473 let def = resolver(self.path.clone())?; 486 let def = resolver(self.path.clone())?;
474 Some(def.as_call_id(db, MacroCallKind::Attr(self.ast_id))) 487 Some(def.as_lazy_macro(db, MacroCallKind::Attr(self.ast_id)).into())
475 } 488 }
476} 489}
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs
index d0e3eaf7e..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 {
@@ -248,7 +248,7 @@ mod tests {
248 kind: MacroCallKind::Attr(AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]))), 248 kind: MacroCallKind::Attr(AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]))),
249 }; 249 };
250 250
251 let id = db.intern_macro(loc); 251 let id: MacroCallId = db.intern_macro(loc).into();
252 let parsed = db.parse_or_expand(id.as_file()).unwrap(); 252 let parsed = db.parse_or_expand(id.as_file()).unwrap();
253 253
254 // FIXME text() for syntax nodes parsed from token tree looks weird 254 // FIXME text() for syntax nodes parsed from token tree looks weird
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index f2bb0bddb..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,7 +114,7 @@ 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 loc = db.lookup_intern_macro(id); 120 let loc = db.lookup_intern_macro(id);
@@ -99,7 +136,7 @@ fn stringify_expand(
99 136
100fn env_expand( 137fn env_expand(
101 _db: &dyn AstDatabase, 138 _db: &dyn AstDatabase,
102 _id: MacroCallId, 139 _id: LazyMacroId,
103 _tt: &tt::Subtree, 140 _tt: &tt::Subtree,
104) -> Result<tt::Subtree, mbe::ExpandError> { 141) -> Result<tt::Subtree, mbe::ExpandError> {
105 // dummy implementation for type-checking purposes 142 // dummy implementation for type-checking purposes
@@ -110,7 +147,7 @@ fn env_expand(
110 147
111fn option_env_expand( 148fn option_env_expand(
112 _db: &dyn AstDatabase, 149 _db: &dyn AstDatabase,
113 _id: MacroCallId, 150 _id: LazyMacroId,
114 _tt: &tt::Subtree, 151 _tt: &tt::Subtree,
115) -> Result<tt::Subtree, mbe::ExpandError> { 152) -> Result<tt::Subtree, mbe::ExpandError> {
116 // dummy implementation for type-checking purposes 153 // dummy implementation for type-checking purposes
@@ -121,7 +158,7 @@ fn option_env_expand(
121 158
122fn column_expand( 159fn column_expand(
123 _db: &dyn AstDatabase, 160 _db: &dyn AstDatabase,
124 _id: MacroCallId, 161 _id: LazyMacroId,
125 _tt: &tt::Subtree, 162 _tt: &tt::Subtree,
126) -> Result<tt::Subtree, mbe::ExpandError> { 163) -> Result<tt::Subtree, mbe::ExpandError> {
127 // dummy implementation for type-checking purposes 164 // dummy implementation for type-checking purposes
@@ -135,7 +172,7 @@ fn column_expand(
135 172
136fn file_expand( 173fn file_expand(
137 _db: &dyn AstDatabase, 174 _db: &dyn AstDatabase,
138 _id: MacroCallId, 175 _id: LazyMacroId,
139 _tt: &tt::Subtree, 176 _tt: &tt::Subtree,
140) -> Result<tt::Subtree, mbe::ExpandError> { 177) -> Result<tt::Subtree, mbe::ExpandError> {
141 // FIXME: RA purposefully lacks knowledge of absolute file names 178 // FIXME: RA purposefully lacks knowledge of absolute file names
@@ -151,7 +188,7 @@ fn file_expand(
151 188
152fn compile_error_expand( 189fn compile_error_expand(
153 _db: &dyn AstDatabase, 190 _db: &dyn AstDatabase,
154 _id: MacroCallId, 191 _id: LazyMacroId,
155 tt: &tt::Subtree, 192 tt: &tt::Subtree,
156) -> Result<tt::Subtree, mbe::ExpandError> { 193) -> Result<tt::Subtree, mbe::ExpandError> {
157 if tt.count() == 1 { 194 if tt.count() == 1 {
@@ -168,7 +205,7 @@ fn compile_error_expand(
168 205
169fn format_args_expand( 206fn format_args_expand(
170 _db: &dyn AstDatabase, 207 _db: &dyn AstDatabase,
171 _id: MacroCallId, 208 _id: LazyMacroId,
172 tt: &tt::Subtree, 209 tt: &tt::Subtree,
173) -> Result<tt::Subtree, mbe::ExpandError> { 210) -> Result<tt::Subtree, mbe::ExpandError> {
174 // We expand `format_args!("", a1, a2)` to 211 // We expand `format_args!("", a1, a2)` to
@@ -208,23 +245,44 @@ fn format_args_expand(
208 Ok(expanded) 245 Ok(expanded)
209} 246}
210 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
211#[cfg(test)] 269#[cfg(test)]
212mod tests { 270mod tests {
213 use super::*; 271 use super::*;
214 use crate::{name::AsName, test_db::TestDB, AstNode, MacroCallKind, MacroCallLoc}; 272 use crate::{name::AsName, test_db::TestDB, AstNode, MacroCallId, MacroCallKind, MacroCallLoc};
215 use ra_db::{fixture::WithFixture, SourceDatabase}; 273 use ra_db::{fixture::WithFixture, SourceDatabase};
216 use ra_syntax::ast::NameOwner; 274 use ra_syntax::ast::NameOwner;
217 275
218 fn expand_builtin_macro(s: &str) -> String { 276 fn expand_builtin_macro(ra_fixture: &str) -> String {
219 let (db, file_id) = TestDB::with_single_file(&s); 277 let (db, file_id) = TestDB::with_single_file(&ra_fixture);
220 let parsed = db.parse(file_id); 278 let parsed = db.parse(file_id);
221 let macro_calls: Vec<_> = 279 let macro_calls: Vec<_> =
222 parsed.syntax_node().descendants().filter_map(ast::MacroCall::cast).collect(); 280 parsed.syntax_node().descendants().filter_map(ast::MacroCall::cast).collect();
223 281
224 let ast_id_map = db.ast_id_map(file_id.into()); 282 let ast_id_map = db.ast_id_map(file_id.into());
225 283
226 let expander = 284 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap();
227 BuiltinFnLikeExpander::by_name(&macro_calls[0].name().unwrap().as_name()).unwrap(); 285 let expander = expander.left().unwrap();
228 286
229 // the first one should be a macro_rules 287 // the first one should be a macro_rules
230 let def = MacroDefId { 288 let def = MacroDefId {
@@ -241,7 +299,7 @@ mod tests {
241 )), 299 )),
242 }; 300 };
243 301
244 let id = db.intern_macro(loc); 302 let id: MacroCallId = db.intern_macro(loc).into();
245 let parsed = db.parse_or_expand(id.as_file()).unwrap(); 303 let parsed = db.parse_or_expand(id.as_file()).unwrap();
246 304
247 parsed.text().to_string() 305 parsed.text().to_string()
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index 70d969238..32e0d5ced 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 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 {
@@ -60,12 +61,15 @@ pub trait AstDatabase: SourceDatabase {
60 fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; 61 fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
61 62
62 #[salsa::interned] 63 #[salsa::interned]
63 fn intern_macro(&self, macro_call: MacroCallLoc) -> MacroCallId; 64 fn intern_macro(&self, macro_call: MacroCallLoc) -> LazyMacroId;
64 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; 65 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
65 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>; 66 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>;
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
@@ -108,6 +113,13 @@ pub(crate) fn macro_arg(
108 db: &dyn AstDatabase, 113 db: &dyn AstDatabase,
109 id: MacroCallId, 114 id: MacroCallId,
110) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { 115) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
116 let id = match id {
117 MacroCallId::LazyMacro(id) => id,
118 MacroCallId::EagerMacro(_id) => {
119 // FIXME: support macro_arg for eager macro
120 return None;
121 }
122 };
111 let loc = db.lookup_intern_macro(id); 123 let loc = db.lookup_intern_macro(id);
112 let arg = loc.kind.arg(db)?; 124 let arg = loc.kind.arg(db)?;
113 let (tt, tmap) = mbe::syntax_node_to_token_tree(&arg)?; 125 let (tt, tmap) = mbe::syntax_node_to_token_tree(&arg)?;
@@ -118,11 +130,18 @@ pub(crate) fn macro_expand(
118 db: &dyn AstDatabase, 130 db: &dyn AstDatabase,
119 id: MacroCallId, 131 id: MacroCallId,
120) -> Result<Arc<tt::Subtree>, String> { 132) -> Result<Arc<tt::Subtree>, String> {
121 let loc = db.lookup_intern_macro(id); 133 let lazy_id = match id {
134 MacroCallId::LazyMacro(id) => id,
135 MacroCallId::EagerMacro(id) => {
136 return Ok(db.lookup_intern_eager_expansion(id).subtree);
137 }
138 };
139
140 let loc = db.lookup_intern_macro(lazy_id);
122 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")?;
123 142
124 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")?;
125 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))?;
126 // Set a hard limit for the expanded tt 145 // Set a hard limit for the expanded tt
127 let count = tt.count(); 146 let count = tt.count();
128 if count > 65536 { 147 if count > 65536 {
@@ -153,9 +172,20 @@ pub(crate) fn parse_macro(
153 // Note: 172 // Note:
154 // The final goal we would like to make all parse_macro success, 173 // The final goal we would like to make all parse_macro success,
155 // such that the following log will not call anyway. 174 // such that the following log will not call anyway.
156 let loc: MacroCallLoc = db.lookup_intern_macro(macro_call_id); 175 match macro_call_id {
157 let node = loc.kind.node(db); 176 MacroCallId::LazyMacro(id) => {
158 log::warn!("fail on macro_parse: (reason: {} macro_call: {:#})", err, node.value); 177 let loc: MacroCallLoc = db.lookup_intern_macro(id);
178 let node = loc.kind.node(db);
179 log::warn!(
180 "fail on macro_parse: (reason: {} macro_call: {:#})",
181 err,
182 node.value
183 );
184 }
185 _ => {
186 log::warn!("fail on macro_parse: (reason: {})", err);
187 }
188 }
159 }) 189 })
160 .ok()?; 190 .ok()?;
161 191
@@ -167,8 +197,14 @@ pub(crate) fn parse_macro(
167 197
168/// Given a `MacroCallId`, return what `FragmentKind` it belongs to. 198/// Given a `MacroCallId`, return what `FragmentKind` it belongs to.
169/// FIXME: Not completed 199/// FIXME: Not completed
170fn to_fragment_kind(db: &dyn AstDatabase, macro_call_id: MacroCallId) -> FragmentKind { 200fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
171 let syn = db.lookup_intern_macro(macro_call_id).kind.node(db).value; 201 let lazy_id = match id {
202 MacroCallId::LazyMacro(id) => id,
203 MacroCallId::EagerMacro(id) => {
204 return db.lookup_intern_eager_expansion(id).fragment;
205 }
206 };
207 let syn = db.lookup_intern_macro(lazy_id).kind.node(db).value;
172 208
173 let parent = match syn.parent() { 209 let parent = match syn.parent() {
174 Some(it) => it, 210 Some(it) => it,
diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs
new file mode 100644
index 000000000..7fcdfab5a
--- /dev/null
+++ b/crates/ra_hir_expand/src/eager.rs
@@ -0,0 +1,112 @@
1//! Eager expansion related utils
2//!
3//! Here is a dump of a discussion from Vadim Petrochenkov about Eager Expansion and
4//! Its name resolution :
5//!
6//! > Eagerly expanded macros (and also macros eagerly expanded by eagerly expanded macros,
7//! > which actually happens in practice too!) are resolved at the location of the "root" macro
8//! > that performs the eager expansion on its arguments.
9//! > If some name cannot be resolved at the eager expansion time it's considered unresolved,
10//! > even if becomes available later (e.g. from a glob import or other macro).
11//!
12//! > Eagerly expanded macros don't add anything to the module structure of the crate and
13//! > don't build any speculative module structures, i.e. they are expanded in a "flat"
14//! > way even if tokens in them look like modules.
15//!
16//! > In other words, it kinda works for simple cases for which it was originally intended,
17//! > and we need to live with it because it's available on stable and widely relied upon.
18//!
19//!
20//! See the full discussion : https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros
21
22use crate::{
23 ast::{self, AstNode},
24 db::AstDatabase,
25 EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
26};
27
28use ra_parser::FragmentKind;
29use ra_syntax::{algo::replace_descendants, SyntaxElement, SyntaxNode};
30use std::{collections::HashMap, sync::Arc};
31
32pub fn expand_eager_macro(
33 db: &impl AstDatabase,
34 macro_call: InFile<ast::MacroCall>,
35 def: MacroDefId,
36 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
37) -> Option<EagerMacroId> {
38 let args = macro_call.value.token_tree()?;
39 let parsed_args = mbe::ast_to_token_tree(&args)?.0;
40 let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0;
41 let result = eager_macro_recur(db, macro_call.with_value(parsed_args.syntax_node()), resolver)?;
42
43 let subtree = to_subtree(&result)?;
44
45 if let MacroDefKind::BuiltInEager(eager) = def.kind {
46 let (subtree, fragment) = eager.expand(&subtree).ok()?;
47 let eager =
48 EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id };
49
50 Some(db.intern_eager_expansion(eager))
51 } else {
52 None
53 }
54}
55
56fn to_subtree(node: &SyntaxNode) -> Option<tt::Subtree> {
57 let mut subtree = mbe::syntax_node_to_token_tree(node)?.0;
58 subtree.delimiter = None;
59 Some(subtree)
60}
61
62fn lazy_expand(
63 db: &impl AstDatabase,
64 def: &MacroDefId,
65 macro_call: InFile<ast::MacroCall>,
66) -> Option<InFile<SyntaxNode>> {
67 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
68
69 let id: MacroCallId =
70 def.as_lazy_macro(db, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
71
72 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node))
73}
74
75fn eager_macro_recur(
76 db: &impl AstDatabase,
77 curr: InFile<SyntaxNode>,
78 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
79) -> Option<SyntaxNode> {
80 let mut original = curr.value.clone();
81
82 let children = curr.value.descendants().filter_map(ast::MacroCall::cast);
83 let mut replaces: HashMap<SyntaxElement, SyntaxElement> = HashMap::default();
84
85 // Collect replacement
86 for child in children {
87 let def: MacroDefId = macro_resolver(child.path()?)?;
88 let insert = match def.kind {
89 MacroDefKind::BuiltInEager(_) => {
90 let id: MacroCallId =
91 expand_eager_macro(db, curr.with_value(child.clone()), def, macro_resolver)?
92 .into();
93 db.parse_or_expand(id.as_file())?
94 }
95 MacroDefKind::Declarative
96 | MacroDefKind::BuiltIn(_)
97 | MacroDefKind::BuiltInDerive(_) => {
98 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?;
99 // replace macro inside
100 eager_macro_recur(db, expanded, macro_resolver)?
101 }
102 };
103
104 replaces.insert(child.syntax().clone().into(), insert.into());
105 }
106
107 if !replaces.is_empty() {
108 original = replace_descendants(&original, |n| replaces.get(n).cloned());
109 }
110
111 Some(original)
112}
diff --git a/crates/ra_hir_expand/src/hygiene.rs b/crates/ra_hir_expand/src/hygiene.rs
index 2e8a533f7..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,14 +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 loc = db.lookup_intern_macro(macro_file.macro_call_id); 26 MacroCallId::LazyMacro(id) => {
27 match loc.def.kind { 27 let loc = db.lookup_intern_macro(id);
28 MacroDefKind::Declarative => loc.def.krate, 28 match loc.def.kind {
29 MacroDefKind::BuiltIn(_) => None, 29 MacroDefKind::Declarative => loc.def.krate,
30 MacroDefKind::BuiltInDerive(_) => None, 30 MacroDefKind::BuiltIn(_) => None,
31 MacroDefKind::BuiltInDerive(_) => None,
32 MacroDefKind::BuiltInEager(_) => None,
33 }
31 } 34 }
32 } 35 MacroCallId::EagerMacro(_id) => None,
36 },
33 }; 37 };
34 Hygiene { def_crate } 38 Hygiene { def_crate }
35 } 39 }
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index 9506f2e1c..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,8 +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 loc = db.lookup_intern_macro(macro_file.macro_call_id); 74 let file_id = match macro_file.macro_call_id {
74 loc.kind.file_id().original_file(db) 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 }
83 };
84 file_id.original_file(db)
75 } 85 }
76 } 86 }
77 } 87 }
@@ -81,7 +91,14 @@ impl HirFileId {
81 match self.0 { 91 match self.0 {
82 HirFileIdRepr::FileId(_) => None, 92 HirFileIdRepr::FileId(_) => None,
83 HirFileIdRepr::MacroFile(macro_file) => { 93 HirFileIdRepr::MacroFile(macro_file) => {
84 let loc = db.lookup_intern_macro(macro_file.macro_call_id); 94 let lazy_id = match macro_file.macro_call_id {
95 MacroCallId::LazyMacro(id) => id,
96 MacroCallId::EagerMacro(_id) => {
97 // FIXME: handle call node for eager macro
98 return None;
99 }
100 };
101 let loc = db.lookup_intern_macro(lazy_id);
85 Some(loc.kind.node(db)) 102 Some(loc.kind.node(db))
86 } 103 }
87 } 104 }
@@ -92,7 +109,14 @@ impl HirFileId {
92 match self.0 { 109 match self.0 {
93 HirFileIdRepr::FileId(_) => None, 110 HirFileIdRepr::FileId(_) => None,
94 HirFileIdRepr::MacroFile(macro_file) => { 111 HirFileIdRepr::MacroFile(macro_file) => {
95 let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); 112 let lazy_id = match macro_file.macro_call_id {
113 MacroCallId::LazyMacro(id) => id,
114 MacroCallId::EagerMacro(_id) => {
115 // FIXME: handle expansion_info for eager macro
116 return None;
117 }
118 };
119 let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id);
96 120
97 let arg_tt = loc.kind.arg(db)?; 121 let arg_tt = loc.kind.arg(db)?;
98 let def_tt = loc.def.ast_id?.to_node(db).token_tree()?; 122 let def_tt = loc.def.ast_id?.to_node(db).token_tree()?;
@@ -118,7 +142,13 @@ impl HirFileId {
118 match self.0 { 142 match self.0 {
119 HirFileIdRepr::FileId(_) => None, 143 HirFileIdRepr::FileId(_) => None,
120 HirFileIdRepr::MacroFile(macro_file) => { 144 HirFileIdRepr::MacroFile(macro_file) => {
121 let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); 145 let lazy_id = match macro_file.macro_call_id {
146 MacroCallId::LazyMacro(id) => id,
147 MacroCallId::EagerMacro(_id) => {
148 return None;
149 }
150 };
151 let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id);
122 let item = match loc.def.kind { 152 let item = match loc.def.kind {
123 MacroDefKind::BuiltInDerive(_) => loc.kind.node(db), 153 MacroDefKind::BuiltInDerive(_) => loc.kind.node(db),
124 _ => return None, 154 _ => return None,
@@ -137,16 +167,44 @@ pub struct MacroFile {
137/// `MacroCallId` identifies a particular macro invocation, like 167/// `MacroCallId` identifies a particular macro invocation, like
138/// `println!("Hello, {}", world)`. 168/// `println!("Hello, {}", world)`.
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 169#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
140pub struct MacroCallId(salsa::InternId); 170pub enum MacroCallId {
141impl salsa::InternKey for MacroCallId { 171 LazyMacro(LazyMacroId),
172 EagerMacro(EagerMacroId),
173}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
176pub struct LazyMacroId(salsa::InternId);
177impl salsa::InternKey for LazyMacroId {
178 fn from_intern_id(v: salsa::InternId) -> Self {
179 LazyMacroId(v)
180 }
181 fn as_intern_id(&self) -> salsa::InternId {
182 self.0
183 }
184}
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
187pub struct EagerMacroId(salsa::InternId);
188impl salsa::InternKey for EagerMacroId {
142 fn from_intern_id(v: salsa::InternId) -> Self { 189 fn from_intern_id(v: salsa::InternId) -> Self {
143 MacroCallId(v) 190 EagerMacroId(v)
144 } 191 }
145 fn as_intern_id(&self) -> salsa::InternId { 192 fn as_intern_id(&self) -> salsa::InternId {
146 self.0 193 self.0
147 } 194 }
148} 195}
149 196
197impl From<LazyMacroId> for MacroCallId {
198 fn from(it: LazyMacroId) -> Self {
199 MacroCallId::LazyMacro(it)
200 }
201}
202impl From<EagerMacroId> for MacroCallId {
203 fn from(it: EagerMacroId) -> Self {
204 MacroCallId::EagerMacro(it)
205 }
206}
207
150#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 208#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
151pub struct MacroDefId { 209pub struct MacroDefId {
152 // FIXME: krate and ast_id are currently optional because we don't have a 210 // FIXME: krate and ast_id are currently optional because we don't have a
@@ -161,7 +219,7 @@ pub struct MacroDefId {
161} 219}
162 220
163impl MacroDefId { 221impl MacroDefId {
164 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 {
165 db.intern_macro(MacroCallLoc { def: self, kind }) 223 db.intern_macro(MacroCallLoc { def: self, kind })
166 } 224 }
167} 225}
@@ -172,6 +230,7 @@ pub enum MacroDefKind {
172 BuiltIn(BuiltinFnLikeExpander), 230 BuiltIn(BuiltinFnLikeExpander),
173 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander 231 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander
174 BuiltInDerive(BuiltinDeriveExpander), 232 BuiltInDerive(BuiltinDeriveExpander),
233 BuiltInEager(EagerExpander),
175} 234}
176 235
177#[derive(Debug, Clone, PartialEq, Eq, Hash)] 236#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -217,6 +276,14 @@ impl MacroCallId {
217 } 276 }
218} 277}
219 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
220/// 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
221#[derive(Debug, Clone, PartialEq, Eq)] 288#[derive(Debug, Clone, PartialEq, Eq)]
222pub struct ExpansionInfo { 289pub struct ExpansionInfo {
@@ -230,6 +297,7 @@ pub struct ExpansionInfo {
230} 297}
231 298
232pub use mbe::Origin; 299pub use mbe::Origin;
300use ra_parser::FragmentKind;
233 301
234impl ExpansionInfo { 302impl ExpansionInfo {
235 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,
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs
index 53cd81d46..55386c030 100644
--- a/crates/ra_hir_ty/src/tests/macros.rs
+++ b/crates/ra_hir_ty/src/tests/macros.rs
@@ -420,6 +420,25 @@ fn main() {
420} 420}
421 421
422#[test] 422#[test]
423fn infer_builtin_macros_concat() {
424 assert_snapshot!(
425 infer(r#"
426#[rustc_builtin_macro]
427macro_rules! concat {() => {}}
428
429fn main() {
430 let x = concat!("hello", concat!("world", "!"));
431}
432"#),
433 @r###"
434 ![0; 13) '"helloworld!"': &str
435 [66; 122) '{ ...")); }': ()
436 [76; 77) 'x': &str
437 "###
438 );
439}
440
441#[test]
423fn infer_derive_clone_simple() { 442fn infer_derive_clone_simple() {
424 let (db, pos) = TestDB::with_position( 443 let (db, pos) = TestDB::with_position(
425 r#" 444 r#"
diff --git a/crates/ra_parser/src/lib.rs b/crates/ra_parser/src/lib.rs
index 81055746b..652492c1e 100644
--- a/crates/ra_parser/src/lib.rs
+++ b/crates/ra_parser/src/lib.rs
@@ -83,7 +83,7 @@ pub fn parse(token_source: &mut dyn TokenSource, tree_sink: &mut dyn TreeSink) {
83 parse_from_tokens(token_source, tree_sink, grammar::root); 83 parse_from_tokens(token_source, tree_sink, grammar::root);
84} 84}
85 85
86#[derive(Clone, Copy)] 86#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
87pub enum FragmentKind { 87pub enum FragmentKind {
88 Path, 88 Path,
89 Expr, 89 Expr,
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index 3f11b747f..0da24560e 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -219,7 +219,7 @@ fn unroot(n: SyntaxNode) -> SyntaxNode {
219} 219}
220 220
221pub mod tokens { 221pub mod tokens {
222 use crate::{AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken, T}; 222 use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken, T};
223 use once_cell::sync::Lazy; 223 use once_cell::sync::Lazy;
224 224
225 pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = 225 pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> =
@@ -251,6 +251,12 @@ pub mod tokens {
251 sf.syntax().first_child_or_token().unwrap().into_token().unwrap() 251 sf.syntax().first_child_or_token().unwrap().into_token().unwrap()
252 } 252 }
253 253
254 pub fn literal(text: &str) -> SyntaxToken {
255 assert_eq!(text.trim(), text);
256 let lit: ast::Literal = super::ast_from_text(&format!("fn f() {{ let _ = {}; }}", text));
257 lit.syntax().first_child_or_token().unwrap().into_token().unwrap()
258 }
259
254 pub fn single_newline() -> SyntaxToken { 260 pub fn single_newline() -> SyntaxToken {
255 SOURCE_FILE 261 SOURCE_FILE
256 .tree() 262 .tree()