diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-03-03 17:30:08 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-03-03 17:30:08 +0000 |
commit | 7a322f9afff05b88507a6956a2d84a3abef0a0d6 (patch) | |
tree | f811a7f405edca2b7e0c32666604117ef6486229 /crates | |
parent | 13b25d73b56ede36d1680efc19f5c11b0669b96c (diff) | |
parent | 4d5e80c6c86aa6bfee50e9f8b80b365c3120ed80 (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]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir_def/src/lib.rs | 23 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/builtin_derive.rs | 26 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/builtin_macro.rs | 124 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/db.rs | 58 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/eager.rs | 112 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/hygiene.rs | 20 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/lib.rs | 88 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/name.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/macros.rs | 19 | ||||
-rw-r--r-- | crates/ra_parser/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/make.rs | 8 |
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; | |||
47 | use std::hash::Hash; | 47 | use std::hash::Hash; |
48 | 48 | ||
49 | use hir_expand::{ | 49 | use 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 | }; |
53 | use ra_arena::{impl_arena_id, RawId}; | 53 | use ra_arena::{impl_arena_id, RawId}; |
54 | use ra_db::{impl_intern_key, salsa, CrateId}; | 54 | use 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 | ||
11 | use crate::db::AstDatabase; | 11 | use crate::db::AstDatabase; |
12 | use crate::{name, quote, MacroCallId, MacroDefId, MacroDefKind}; | 12 | use crate::{name, quote, LazyMacroId, MacroDefId, MacroDefKind}; |
13 | 13 | ||
14 | macro_rules! register_builtin { | 14 | macro_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 | ||
156 | fn copy_expand( | 156 | fn 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 | ||
164 | fn clone_expand( | 164 | fn 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 | ||
172 | fn default_expand( | 172 | fn 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 | ||
180 | fn debug_expand( | 180 | fn 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 | ||
188 | fn hash_expand( | 188 | fn 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 | ||
196 | fn eq_expand( | 196 | fn 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 | ||
204 | fn partial_eq_expand( | 204 | fn 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 | ||
212 | fn ord_expand( | 212 | fn 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 | ||
220 | fn partial_ord_expand( | 220 | fn 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)] |
229 | mod tests { | 229 | mod 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 |
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,7 +114,7 @@ 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 loc = db.lookup_intern_macro(id); | 120 | let loc = db.lookup_intern_macro(id); |
@@ -99,7 +136,7 @@ fn stringify_expand( | |||
99 | 136 | ||
100 | fn env_expand( | 137 | fn 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 | ||
111 | fn option_env_expand( | 148 | fn 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 | ||
122 | fn column_expand( | 159 | fn 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 | ||
136 | fn file_expand( | 173 | fn 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 | ||
152 | fn compile_error_expand( | 189 | fn 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 | ||
169 | fn format_args_expand( | 206 | fn 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 | ||
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 | |||
211 | #[cfg(test)] | 269 | #[cfg(test)] |
212 | mod tests { | 270 | mod 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(¯o_calls[0].name().unwrap().as_name()).unwrap(); |
227 | BuiltinFnLikeExpander::by_name(¯o_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; | |||
9 | use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode}; | 9 | use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode}; |
10 | 10 | ||
11 | use crate::{ | 11 | use 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 | ||
71 | pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { | 75 | pub(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, ¯o_arg.0).map_err(|err| format!("{:?}", err))?; | 144 | let tt = macro_rules.0.expand(db, lazy_id, ¯o_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 |
170 | fn to_fragment_kind(db: &dyn AstDatabase, macro_call_id: MacroCallId) -> FragmentKind { | 200 | fn 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 | |||
22 | use crate::{ | ||
23 | ast::{self, AstNode}, | ||
24 | db::AstDatabase, | ||
25 | EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, | ||
26 | }; | ||
27 | |||
28 | use ra_parser::FragmentKind; | ||
29 | use ra_syntax::{algo::replace_descendants, SyntaxElement, SyntaxNode}; | ||
30 | use std::{collections::HashMap, sync::Arc}; | ||
31 | |||
32 | pub 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 | |||
56 | fn 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 | |||
62 | fn 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(¯o_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 | |||
75 | fn 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; | |||
9 | use crate::{ | 9 | use 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; | |||
12 | pub mod builtin_derive; | 12 | pub mod builtin_derive; |
13 | pub mod builtin_macro; | 13 | pub mod builtin_macro; |
14 | pub mod quote; | 14 | pub mod quote; |
15 | pub mod eager; | ||
15 | 16 | ||
16 | use std::hash::Hash; | 17 | use std::hash::Hash; |
17 | use std::sync::Arc; | 18 | use std::sync::Arc; |
@@ -25,7 +26,7 @@ use ra_syntax::{ | |||
25 | 26 | ||
26 | use crate::ast_id_map::FileAstId; | 27 | use crate::ast_id_map::FileAstId; |
27 | use crate::builtin_derive::BuiltinDeriveExpander; | 28 | use crate::builtin_derive::BuiltinDeriveExpander; |
28 | use crate::builtin_macro::BuiltinFnLikeExpander; | 29 | use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander}; |
29 | 30 | ||
30 | #[cfg(test)] | 31 | #[cfg(test)] |
31 | mod test_db; | 32 | mod 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)] |
140 | pub struct MacroCallId(salsa::InternId); | 170 | pub enum MacroCallId { |
141 | impl salsa::InternKey for MacroCallId { | 171 | LazyMacro(LazyMacroId), |
172 | EagerMacro(EagerMacroId), | ||
173 | } | ||
174 | |||
175 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
176 | pub struct LazyMacroId(salsa::InternId); | ||
177 | impl 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)] | ||
187 | pub struct EagerMacroId(salsa::InternId); | ||
188 | impl 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 | ||
197 | impl From<LazyMacroId> for MacroCallId { | ||
198 | fn from(it: LazyMacroId) -> Self { | ||
199 | MacroCallId::LazyMacro(it) | ||
200 | } | ||
201 | } | ||
202 | impl 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)] |
151 | pub struct MacroDefId { | 209 | pub 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 | ||
163 | impl MacroDefId { | 221 | impl 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)] | ||
280 | pub 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)] |
222 | pub struct ExpansionInfo { | 289 | pub struct ExpansionInfo { |
@@ -230,6 +297,7 @@ pub struct ExpansionInfo { | |||
230 | } | 297 | } |
231 | 298 | ||
232 | pub use mbe::Origin; | 299 | pub use mbe::Origin; |
300 | use ra_parser::FragmentKind; | ||
233 | 301 | ||
234 | impl ExpansionInfo { | 302 | impl 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] |
423 | fn infer_builtin_macros_concat() { | ||
424 | assert_snapshot!( | ||
425 | infer(r#" | ||
426 | #[rustc_builtin_macro] | ||
427 | macro_rules! concat {() => {}} | ||
428 | |||
429 | fn 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] | ||
423 | fn infer_derive_clone_simple() { | 442 | fn 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)] |
87 | pub enum FragmentKind { | 87 | pub 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 | ||
221 | pub mod tokens { | 221 | pub 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() |