aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_expand/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_expand/src')
-rw-r--r--crates/ra_hir_expand/src/ast_id_map.rs119
-rw-r--r--crates/ra_hir_expand/src/builtin_derive.rs361
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs649
-rw-r--r--crates/ra_hir_expand/src/db.rs404
-rw-r--r--crates/ra_hir_expand/src/diagnostics.rs110
-rw-r--r--crates/ra_hir_expand/src/eager.rs144
-rw-r--r--crates/ra_hir_expand/src/hygiene.rs66
-rw-r--r--crates/ra_hir_expand/src/lib.rs452
-rw-r--r--crates/ra_hir_expand/src/name.rs230
-rw-r--r--crates/ra_hir_expand/src/proc_macro.rs143
-rw-r--r--crates/ra_hir_expand/src/quote.rs282
-rw-r--r--crates/ra_hir_expand/src/test_db.rs49
12 files changed, 0 insertions, 3009 deletions
diff --git a/crates/ra_hir_expand/src/ast_id_map.rs b/crates/ra_hir_expand/src/ast_id_map.rs
deleted file mode 100644
index 8bfe1b4ba..000000000
--- a/crates/ra_hir_expand/src/ast_id_map.rs
+++ /dev/null
@@ -1,119 +0,0 @@
1//! `AstIdMap` allows to create stable IDs for "large" syntax nodes like items
2//! and macro calls.
3//!
4//! Specifically, it enumerates all items in a file and uses position of a an
5//! item as an ID. That way, id's don't change unless the set of items itself
6//! changes.
7
8use std::{
9 any::type_name,
10 fmt,
11 hash::{Hash, Hasher},
12 marker::PhantomData,
13};
14
15use ra_arena::{Arena, Idx};
16use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
17
18/// `AstId` points to an AST node in a specific file.
19pub struct FileAstId<N: AstNode> {
20 raw: ErasedFileAstId,
21 _ty: PhantomData<fn() -> N>,
22}
23
24impl<N: AstNode> Clone for FileAstId<N> {
25 fn clone(&self) -> FileAstId<N> {
26 *self
27 }
28}
29impl<N: AstNode> Copy for FileAstId<N> {}
30
31impl<N: AstNode> PartialEq for FileAstId<N> {
32 fn eq(&self, other: &Self) -> bool {
33 self.raw == other.raw
34 }
35}
36impl<N: AstNode> Eq for FileAstId<N> {}
37impl<N: AstNode> Hash for FileAstId<N> {
38 fn hash<H: Hasher>(&self, hasher: &mut H) {
39 self.raw.hash(hasher);
40 }
41}
42
43impl<N: AstNode> fmt::Debug for FileAstId<N> {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(f, "FileAstId::<{}>({})", type_name::<N>(), self.raw.into_raw())
46 }
47}
48
49impl<N: AstNode> FileAstId<N> {
50 // Can't make this a From implementation because of coherence
51 pub fn upcast<M: AstNode>(self) -> FileAstId<M>
52 where
53 N: Into<M>,
54 {
55 FileAstId { raw: self.raw, _ty: PhantomData }
56 }
57}
58
59type ErasedFileAstId = Idx<SyntaxNodePtr>;
60
61/// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back.
62#[derive(Debug, PartialEq, Eq, Default)]
63pub struct AstIdMap {
64 arena: Arena<SyntaxNodePtr>,
65}
66
67impl AstIdMap {
68 pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap {
69 assert!(node.parent().is_none());
70 let mut res = AstIdMap { arena: Arena::default() };
71 // By walking the tree in breadth-first order we make sure that parents
72 // get lower ids then children. That is, adding a new child does not
73 // change parent's id. This means that, say, adding a new function to a
74 // trait does not change ids of top-level items, which helps caching.
75 bfs(node, |it| {
76 if let Some(module_item) = ast::Item::cast(it) {
77 res.alloc(module_item.syntax());
78 }
79 });
80 res
81 }
82
83 pub fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> {
84 let raw = self.erased_ast_id(item.syntax());
85 FileAstId { raw, _ty: PhantomData }
86 }
87 fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
88 let ptr = SyntaxNodePtr::new(item);
89 match self.arena.iter().find(|(_id, i)| **i == ptr) {
90 Some((it, _)) => it,
91 None => panic!(
92 "Can't find {:?} in AstIdMap:\n{:?}",
93 item,
94 self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
95 ),
96 }
97 }
98
99 pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
100 self.arena[id.raw].clone().cast::<N>().unwrap()
101 }
102
103 fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId {
104 self.arena.alloc(SyntaxNodePtr::new(item))
105 }
106}
107
108/// Walks the subtree in bfs order, calling `f` for each node.
109fn bfs(node: &SyntaxNode, mut f: impl FnMut(SyntaxNode)) {
110 let mut curr_layer = vec![node.clone()];
111 let mut next_layer = vec![];
112 while !curr_layer.is_empty() {
113 curr_layer.drain(..).for_each(|node| {
114 next_layer.extend(node.children());
115 f(node);
116 });
117 std::mem::swap(&mut curr_layer, &mut next_layer);
118 }
119}
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs
deleted file mode 100644
index 69fa907cb..000000000
--- a/crates/ra_hir_expand/src/builtin_derive.rs
+++ /dev/null
@@ -1,361 +0,0 @@
1//! Builtin derives.
2
3use log::debug;
4
5use ra_parser::FragmentKind;
6use ra_syntax::{
7 ast::{self, AstNode, GenericParamsOwner, ModuleItemOwner, NameOwner},
8 match_ast,
9};
10
11use crate::{db::AstDatabase, name, quote, LazyMacroId, MacroDefId, MacroDefKind};
12
13macro_rules! register_builtin {
14 ( $($trait:ident => $expand:ident),* ) => {
15 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16 pub enum BuiltinDeriveExpander {
17 $($trait),*
18 }
19
20 impl BuiltinDeriveExpander {
21 pub fn expand(
22 &self,
23 db: &dyn AstDatabase,
24 id: LazyMacroId,
25 tt: &tt::Subtree,
26 ) -> Result<tt::Subtree, mbe::ExpandError> {
27 let expander = match *self {
28 $( BuiltinDeriveExpander::$trait => $expand, )*
29 };
30 expander(db, id, tt)
31 }
32 }
33
34 pub fn find_builtin_derive(ident: &name::Name) -> Option<MacroDefId> {
35 let kind = match ident {
36 $( id if id == &name::name![$trait] => BuiltinDeriveExpander::$trait, )*
37 _ => return None,
38 };
39
40 Some(MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(kind), local_inner: false })
41 }
42 };
43}
44
45register_builtin! {
46 Copy => copy_expand,
47 Clone => clone_expand,
48 Default => default_expand,
49 Debug => debug_expand,
50 Hash => hash_expand,
51 Ord => ord_expand,
52 PartialOrd => partial_ord_expand,
53 Eq => eq_expand,
54 PartialEq => partial_eq_expand
55}
56
57struct BasicAdtInfo {
58 name: tt::Ident,
59 type_params: usize,
60}
61
62fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> {
63 let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, FragmentKind::Items)?; // FragmentKind::Items doesn't parse attrs?
64 let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
65 debug!("derive node didn't parse");
66 mbe::ExpandError::UnexpectedToken
67 })?;
68 let item = macro_items.items().next().ok_or_else(|| {
69 debug!("no module item parsed");
70 mbe::ExpandError::NoMatchingRule
71 })?;
72 let node = item.syntax();
73 let (name, params) = match_ast! {
74 match node {
75 ast::Struct(it) => (it.name(), it.generic_param_list()),
76 ast::Enum(it) => (it.name(), it.generic_param_list()),
77 ast::Union(it) => (it.name(), it.generic_param_list()),
78 _ => {
79 debug!("unexpected node is {:?}", node);
80 return Err(mbe::ExpandError::ConversionError)
81 },
82 }
83 };
84 let name = name.ok_or_else(|| {
85 debug!("parsed item has no name");
86 mbe::ExpandError::NoMatchingRule
87 })?;
88 let name_token_id = token_map.token_by_range(name.syntax().text_range()).ok_or_else(|| {
89 debug!("name token not found");
90 mbe::ExpandError::ConversionError
91 })?;
92 let name_token = tt::Ident { id: name_token_id, text: name.text().clone() };
93 let type_params = params.map_or(0, |type_param_list| type_param_list.type_params().count());
94 Ok(BasicAdtInfo { name: name_token, type_params })
95}
96
97fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> {
98 let mut result = Vec::<tt::TokenTree>::new();
99 result.push(
100 tt::Leaf::Punct(tt::Punct {
101 char: '<',
102 spacing: tt::Spacing::Alone,
103 id: tt::TokenId::unspecified(),
104 })
105 .into(),
106 );
107 for i in 0..n {
108 if i > 0 {
109 result.push(
110 tt::Leaf::Punct(tt::Punct {
111 char: ',',
112 spacing: tt::Spacing::Alone,
113 id: tt::TokenId::unspecified(),
114 })
115 .into(),
116 );
117 }
118 result.push(
119 tt::Leaf::Ident(tt::Ident {
120 id: tt::TokenId::unspecified(),
121 text: format!("T{}", i).into(),
122 })
123 .into(),
124 );
125 result.extend(bound.iter().cloned());
126 }
127 result.push(
128 tt::Leaf::Punct(tt::Punct {
129 char: '>',
130 spacing: tt::Spacing::Alone,
131 id: tt::TokenId::unspecified(),
132 })
133 .into(),
134 );
135 result
136}
137
138fn expand_simple_derive(
139 tt: &tt::Subtree,
140 trait_path: tt::Subtree,
141) -> Result<tt::Subtree, mbe::ExpandError> {
142 let info = parse_adt(tt)?;
143 let name = info.name;
144 let trait_path_clone = trait_path.token_trees.clone();
145 let bound = (quote! { : ##trait_path_clone }).token_trees;
146 let type_params = make_type_args(info.type_params, bound);
147 let type_args = make_type_args(info.type_params, Vec::new());
148 let trait_path = trait_path.token_trees;
149 let expanded = quote! {
150 impl ##type_params ##trait_path for #name ##type_args {}
151 };
152 Ok(expanded)
153}
154
155fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree {
156 // FIXME: make hygiene works for builtin derive macro
157 // such that $crate can be used here.
158 let cg = db.crate_graph();
159 let krate = db.lookup_intern_macro(id).krate;
160
161 // XXX
162 // All crates except core itself should have a dependency on core,
163 // We detect `core` by seeing whether it doesn't have such a dependency.
164 let tt = if cg[krate].dependencies.iter().any(|dep| &*dep.name == "core") {
165 quote! { core }
166 } else {
167 quote! { crate }
168 };
169
170 tt.token_trees[0].clone()
171}
172
173fn copy_expand(
174 db: &dyn AstDatabase,
175 id: LazyMacroId,
176 tt: &tt::Subtree,
177) -> Result<tt::Subtree, mbe::ExpandError> {
178 let krate = find_builtin_crate(db, id);
179 expand_simple_derive(tt, quote! { #krate::marker::Copy })
180}
181
182fn clone_expand(
183 db: &dyn AstDatabase,
184 id: LazyMacroId,
185 tt: &tt::Subtree,
186) -> Result<tt::Subtree, mbe::ExpandError> {
187 let krate = find_builtin_crate(db, id);
188 expand_simple_derive(tt, quote! { #krate::clone::Clone })
189}
190
191fn default_expand(
192 db: &dyn AstDatabase,
193 id: LazyMacroId,
194 tt: &tt::Subtree,
195) -> Result<tt::Subtree, mbe::ExpandError> {
196 let krate = find_builtin_crate(db, id);
197 expand_simple_derive(tt, quote! { #krate::default::Default })
198}
199
200fn debug_expand(
201 db: &dyn AstDatabase,
202 id: LazyMacroId,
203 tt: &tt::Subtree,
204) -> Result<tt::Subtree, mbe::ExpandError> {
205 let krate = find_builtin_crate(db, id);
206 expand_simple_derive(tt, quote! { #krate::fmt::Debug })
207}
208
209fn hash_expand(
210 db: &dyn AstDatabase,
211 id: LazyMacroId,
212 tt: &tt::Subtree,
213) -> Result<tt::Subtree, mbe::ExpandError> {
214 let krate = find_builtin_crate(db, id);
215 expand_simple_derive(tt, quote! { #krate::hash::Hash })
216}
217
218fn eq_expand(
219 db: &dyn AstDatabase,
220 id: LazyMacroId,
221 tt: &tt::Subtree,
222) -> Result<tt::Subtree, mbe::ExpandError> {
223 let krate = find_builtin_crate(db, id);
224 expand_simple_derive(tt, quote! { #krate::cmp::Eq })
225}
226
227fn partial_eq_expand(
228 db: &dyn AstDatabase,
229 id: LazyMacroId,
230 tt: &tt::Subtree,
231) -> Result<tt::Subtree, mbe::ExpandError> {
232 let krate = find_builtin_crate(db, id);
233 expand_simple_derive(tt, quote! { #krate::cmp::PartialEq })
234}
235
236fn ord_expand(
237 db: &dyn AstDatabase,
238 id: LazyMacroId,
239 tt: &tt::Subtree,
240) -> Result<tt::Subtree, mbe::ExpandError> {
241 let krate = find_builtin_crate(db, id);
242 expand_simple_derive(tt, quote! { #krate::cmp::Ord })
243}
244
245fn partial_ord_expand(
246 db: &dyn AstDatabase,
247 id: LazyMacroId,
248 tt: &tt::Subtree,
249) -> Result<tt::Subtree, mbe::ExpandError> {
250 let krate = find_builtin_crate(db, id);
251 expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd })
252}
253
254#[cfg(test)]
255mod tests {
256 use name::{known, Name};
257 use ra_db::{fixture::WithFixture, CrateId, SourceDatabase};
258
259 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
260
261 use super::*;
262
263 fn expand_builtin_derive(s: &str, name: Name) -> String {
264 let def = find_builtin_derive(&name).unwrap();
265 let fixture = format!(
266 r#"//- /main.rs crate:main deps:core
267<|>
268{}
269//- /lib.rs crate:core
270// empty
271"#,
272 s
273 );
274
275 let (db, file_pos) = TestDB::with_position(&fixture);
276 let file_id = file_pos.file_id;
277 let parsed = db.parse(file_id);
278 let items: Vec<_> =
279 parsed.syntax_node().descendants().filter_map(ast::Item::cast).collect();
280
281 let ast_id_map = db.ast_id_map(file_id.into());
282
283 let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]));
284
285 let loc = MacroCallLoc {
286 def,
287 krate: CrateId(0),
288 kind: MacroCallKind::Attr(attr_id, name.to_string()),
289 };
290
291 let id: MacroCallId = db.intern_macro(loc).into();
292 let parsed = db.parse_or_expand(id.as_file()).unwrap();
293
294 // FIXME text() for syntax nodes parsed from token tree looks weird
295 // because there's no whitespace, see below
296 parsed.text().to_string()
297 }
298
299 #[test]
300 fn test_copy_expand_simple() {
301 let expanded = expand_builtin_derive(
302 r#"
303 #[derive(Copy)]
304 struct Foo;
305"#,
306 known::Copy,
307 );
308
309 assert_eq!(expanded, "impl< >core::marker::CopyforFoo< >{}");
310 }
311
312 #[test]
313 fn test_copy_expand_with_type_params() {
314 let expanded = expand_builtin_derive(
315 r#"
316 #[derive(Copy)]
317 struct Foo<A, B>;
318"#,
319 known::Copy,
320 );
321
322 assert_eq!(
323 expanded,
324 "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"
325 );
326 }
327
328 #[test]
329 fn test_copy_expand_with_lifetimes() {
330 let expanded = expand_builtin_derive(
331 r#"
332 #[derive(Copy)]
333 struct Foo<A, B, 'a, 'b>;
334"#,
335 known::Copy,
336 );
337
338 // We currently just ignore lifetimes
339
340 assert_eq!(
341 expanded,
342 "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"
343 );
344 }
345
346 #[test]
347 fn test_clone_expand() {
348 let expanded = expand_builtin_derive(
349 r#"
350 #[derive(Clone)]
351 struct Foo<A, B>;
352"#,
353 known::Clone,
354 );
355
356 assert_eq!(
357 expanded,
358 "impl<T0:core::clone::Clone,T1:core::clone::Clone>core::clone::CloneforFoo<T0,T1>{}"
359 );
360 }
361}
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
deleted file mode 100644
index 9f50569dc..000000000
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ /dev/null
@@ -1,649 +0,0 @@
1//! Builtin macro
2use crate::{
3 db::AstDatabase, name, quote, AstId, CrateId, EagerMacroId, LazyMacroId, MacroCallId,
4 MacroDefId, MacroDefKind, TextSize,
5};
6
7use either::Either;
8use mbe::parse_to_token_tree;
9use ra_db::FileId;
10use ra_parser::FragmentKind;
11use ra_syntax::ast::{self, AstToken, HasStringValue};
12
13macro_rules! register_builtin {
14 ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
15 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16 pub enum BuiltinFnLikeExpander {
17 $($kind),*
18 }
19
20 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21 pub enum EagerExpander {
22 $($e_kind),*
23 }
24
25 impl BuiltinFnLikeExpander {
26 pub fn expand(
27 &self,
28 db: &dyn AstDatabase,
29 id: LazyMacroId,
30 tt: &tt::Subtree,
31 ) -> Result<tt::Subtree, mbe::ExpandError> {
32 let expander = match *self {
33 $( BuiltinFnLikeExpander::$kind => $expand, )*
34 };
35 expander(db, id, tt)
36 }
37 }
38
39 impl EagerExpander {
40 pub fn expand(
41 &self,
42 db: &dyn AstDatabase,
43 arg_id: EagerMacroId,
44 tt: &tt::Subtree,
45 ) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
46 let expander = match *self {
47 $( EagerExpander::$e_kind => $e_expand, )*
48 };
49 expander(db,arg_id,tt)
50 }
51 }
52
53 fn find_by_name(ident: &name::Name) -> Option<Either<BuiltinFnLikeExpander, EagerExpander>> {
54 match ident {
55 $( id if id == &name::name![$name] => Some(Either::Left(BuiltinFnLikeExpander::$kind)), )*
56 $( id if id == &name::name![$e_name] => Some(Either::Right(EagerExpander::$e_kind)), )*
57 _ => return None,
58 }
59 }
60 };
61}
62
63pub fn find_builtin_macro(
64 ident: &name::Name,
65 krate: CrateId,
66 ast_id: AstId<ast::MacroCall>,
67) -> Option<MacroDefId> {
68 let kind = find_by_name(ident)?;
69
70 match kind {
71 Either::Left(kind) => Some(MacroDefId {
72 krate: Some(krate),
73 ast_id: Some(ast_id),
74 kind: MacroDefKind::BuiltIn(kind),
75 local_inner: false,
76 }),
77 Either::Right(kind) => Some(MacroDefId {
78 krate: Some(krate),
79 ast_id: Some(ast_id),
80 kind: MacroDefKind::BuiltInEager(kind),
81 local_inner: false,
82 }),
83 }
84}
85
86register_builtin! {
87 LAZY:
88 (column, Column) => column_expand,
89 (compile_error, CompileError) => compile_error_expand,
90 (file, File) => file_expand,
91 (line, Line) => line_expand,
92 (assert, Assert) => assert_expand,
93 (stringify, Stringify) => stringify_expand,
94 (format_args, FormatArgs) => format_args_expand,
95 // format_args_nl only differs in that it adds a newline in the end,
96 // so we use the same stub expansion for now
97 (format_args_nl, FormatArgsNl) => format_args_expand,
98
99 EAGER:
100 (concat, Concat) => concat_expand,
101 (include, Include) => include_expand,
102 (include_bytes, IncludeBytes) => include_bytes_expand,
103 (include_str, IncludeStr) => include_str_expand,
104 (env, Env) => env_expand,
105 (option_env, OptionEnv) => option_env_expand
106}
107
108fn line_expand(
109 _db: &dyn AstDatabase,
110 _id: LazyMacroId,
111 _tt: &tt::Subtree,
112) -> Result<tt::Subtree, mbe::ExpandError> {
113 // dummy implementation for type-checking purposes
114 let line_num = 0;
115 let expanded = quote! {
116 #line_num
117 };
118
119 Ok(expanded)
120}
121
122fn stringify_expand(
123 db: &dyn AstDatabase,
124 id: LazyMacroId,
125 _tt: &tt::Subtree,
126) -> Result<tt::Subtree, mbe::ExpandError> {
127 let loc = db.lookup_intern_macro(id);
128
129 let macro_content = {
130 let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
131 let macro_args = arg;
132 let text = macro_args.text();
133 let without_parens = TextSize::of('(')..text.len() - TextSize::of(')');
134 text.slice(without_parens).to_string()
135 };
136
137 let expanded = quote! {
138 #macro_content
139 };
140
141 Ok(expanded)
142}
143
144fn column_expand(
145 _db: &dyn AstDatabase,
146 _id: LazyMacroId,
147 _tt: &tt::Subtree,
148) -> Result<tt::Subtree, mbe::ExpandError> {
149 // dummy implementation for type-checking purposes
150 let col_num = 0;
151 let expanded = quote! {
152 #col_num
153 };
154
155 Ok(expanded)
156}
157
158fn assert_expand(
159 _db: &dyn AstDatabase,
160 _id: LazyMacroId,
161 tt: &tt::Subtree,
162) -> Result<tt::Subtree, mbe::ExpandError> {
163 // A hacky implementation for goto def and hover
164 // We expand `assert!(cond, arg1, arg2)` to
165 // ```
166 // {(cond, &(arg1), &(arg2));}
167 // ```,
168 // which is wrong but useful.
169
170 let mut args = Vec::new();
171 let mut current = Vec::new();
172 for tt in tt.token_trees.iter().cloned() {
173 match tt {
174 tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
175 args.push(current);
176 current = Vec::new();
177 }
178 _ => {
179 current.push(tt);
180 }
181 }
182 }
183 if !current.is_empty() {
184 args.push(current);
185 }
186
187 let arg_tts = args.into_iter().flat_map(|arg| {
188 quote! { &(##arg), }
189 }.token_trees).collect::<Vec<_>>();
190
191 let expanded = quote! {
192 { { (##arg_tts); } }
193 };
194 Ok(expanded)
195}
196
197fn file_expand(
198 _db: &dyn AstDatabase,
199 _id: LazyMacroId,
200 _tt: &tt::Subtree,
201) -> Result<tt::Subtree, mbe::ExpandError> {
202 // FIXME: RA purposefully lacks knowledge of absolute file names
203 // so just return "".
204 let file_name = "";
205
206 let expanded = quote! {
207 #file_name
208 };
209
210 Ok(expanded)
211}
212
213fn compile_error_expand(
214 _db: &dyn AstDatabase,
215 _id: LazyMacroId,
216 tt: &tt::Subtree,
217) -> Result<tt::Subtree, mbe::ExpandError> {
218 if tt.count() == 1 {
219 if let tt::TokenTree::Leaf(tt::Leaf::Literal(it)) = &tt.token_trees[0] {
220 let s = it.text.as_str();
221 if s.contains('"') {
222 return Ok(quote! { loop { #it }});
223 }
224 };
225 }
226
227 Err(mbe::ExpandError::BindingError("Must be a string".into()))
228}
229
230fn format_args_expand(
231 _db: &dyn AstDatabase,
232 _id: LazyMacroId,
233 tt: &tt::Subtree,
234) -> Result<tt::Subtree, mbe::ExpandError> {
235 // We expand `format_args!("", a1, a2)` to
236 // ```
237 // std::fmt::Arguments::new_v1(&[], &[
238 // std::fmt::ArgumentV1::new(&arg1,std::fmt::Display::fmt),
239 // std::fmt::ArgumentV1::new(&arg2,std::fmt::Display::fmt),
240 // ])
241 // ```,
242 // which is still not really correct, but close enough for now
243 let mut args = Vec::new();
244 let mut current = Vec::new();
245 for tt in tt.token_trees.iter().cloned() {
246 match tt {
247 tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
248 args.push(current);
249 current = Vec::new();
250 }
251 _ => {
252 current.push(tt);
253 }
254 }
255 }
256 if !current.is_empty() {
257 args.push(current);
258 }
259 if args.is_empty() {
260 return Err(mbe::ExpandError::NoMatchingRule);
261 }
262 let _format_string = args.remove(0);
263 let arg_tts = args.into_iter().flat_map(|arg| {
264 quote! { std::fmt::ArgumentV1::new(&(##arg), std::fmt::Display::fmt), }
265 }.token_trees).collect::<Vec<_>>();
266 let expanded = quote! {
267 std::fmt::Arguments::new_v1(&[], &[##arg_tts])
268 };
269 Ok(expanded)
270}
271
272fn unquote_str(lit: &tt::Literal) -> Option<String> {
273 let lit = ast::make::tokens::literal(&lit.to_string());
274 let token = ast::String::cast(lit)?;
275 token.value().map(|it| it.into_owned())
276}
277
278fn concat_expand(
279 _db: &dyn AstDatabase,
280 _arg_id: EagerMacroId,
281 tt: &tt::Subtree,
282) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
283 let mut text = String::new();
284 for (i, t) in tt.token_trees.iter().enumerate() {
285 match t {
286 tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => {
287 text += &unquote_str(&it).ok_or_else(|| mbe::ExpandError::ConversionError)?;
288 }
289 tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
290 _ => return Err(mbe::ExpandError::UnexpectedToken),
291 }
292 }
293
294 Ok((quote!(#text), FragmentKind::Expr))
295}
296
297fn relative_file(
298 db: &dyn AstDatabase,
299 call_id: MacroCallId,
300 path: &str,
301 allow_recursion: bool,
302) -> Option<FileId> {
303 let call_site = call_id.as_file().original_file(db);
304 let res = db.resolve_path(call_site, path)?;
305 // Prevent include itself
306 if res == call_site && !allow_recursion {
307 None
308 } else {
309 Some(res)
310 }
311}
312
313fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> {
314 tt.token_trees
315 .get(0)
316 .and_then(|tt| match tt {
317 tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(&it),
318 _ => None,
319 })
320 .ok_or_else(|| mbe::ExpandError::ConversionError)
321}
322
323fn include_expand(
324 db: &dyn AstDatabase,
325 arg_id: EagerMacroId,
326 tt: &tt::Subtree,
327) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
328 let path = parse_string(tt)?;
329 let file_id = relative_file(db, arg_id.into(), &path, false)
330 .ok_or_else(|| mbe::ExpandError::ConversionError)?;
331
332 // FIXME:
333 // Handle include as expression
334 let res = parse_to_token_tree(&db.file_text(file_id))
335 .ok_or_else(|| mbe::ExpandError::ConversionError)?
336 .0;
337
338 Ok((res, FragmentKind::Items))
339}
340
341fn include_bytes_expand(
342 _db: &dyn AstDatabase,
343 _arg_id: EagerMacroId,
344 tt: &tt::Subtree,
345) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
346 let _path = parse_string(tt)?;
347
348 // FIXME: actually read the file here if the user asked for macro expansion
349 let res = tt::Subtree {
350 delimiter: None,
351 token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
352 text: r#"b"""#.into(),
353 id: tt::TokenId::unspecified(),
354 }))],
355 };
356 Ok((res, FragmentKind::Expr))
357}
358
359fn include_str_expand(
360 db: &dyn AstDatabase,
361 arg_id: EagerMacroId,
362 tt: &tt::Subtree,
363) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
364 let path = parse_string(tt)?;
365
366 // FIXME: we're not able to read excluded files (which is most of them because
367 // it's unusual to `include_str!` a Rust file), but we can return an empty string.
368 // Ideally, we'd be able to offer a precise expansion if the user asks for macro
369 // expansion.
370 let file_id = match relative_file(db, arg_id.into(), &path, true) {
371 Some(file_id) => file_id,
372 None => {
373 return Ok((quote!(""), FragmentKind::Expr));
374 }
375 };
376
377 let text = db.file_text(file_id);
378 let text = &*text;
379
380 Ok((quote!(#text), FragmentKind::Expr))
381}
382
383fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> {
384 let krate = db.lookup_intern_eager_expansion(arg_id).krate;
385 db.crate_graph()[krate].env.get(key)
386}
387
388fn env_expand(
389 db: &dyn AstDatabase,
390 arg_id: EagerMacroId,
391 tt: &tt::Subtree,
392) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
393 let key = parse_string(tt)?;
394
395 // FIXME:
396 // If the environment variable is not defined int rustc, then a compilation error will be emitted.
397 // We might do the same if we fully support all other stuffs.
398 // But for now on, we should return some dummy string for better type infer purpose.
399 // However, we cannot use an empty string here, because for
400 // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
401 // `include!("foo.rs"), which might go to infinite loop
402 let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| "__RA_UNIMPLEMENTED__".to_string());
403 let expanded = quote! { #s };
404
405 Ok((expanded, FragmentKind::Expr))
406}
407
408fn option_env_expand(
409 db: &dyn AstDatabase,
410 arg_id: EagerMacroId,
411 tt: &tt::Subtree,
412) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
413 let key = parse_string(tt)?;
414 let expanded = match get_env_inner(db, arg_id, &key) {
415 None => quote! { std::option::Option::None::<&str> },
416 Some(s) => quote! { std::option::Some(#s) },
417 };
418
419 Ok((expanded, FragmentKind::Expr))
420}
421
422#[cfg(test)]
423mod tests {
424 use super::*;
425 use crate::{
426 name::AsName, test_db::TestDB, AstNode, EagerCallLoc, MacroCallId, MacroCallKind,
427 MacroCallLoc,
428 };
429 use ra_db::{fixture::WithFixture, SourceDatabase};
430 use ra_syntax::ast::NameOwner;
431 use std::sync::Arc;
432
433 fn expand_builtin_macro(ra_fixture: &str) -> String {
434 let (db, file_id) = TestDB::with_single_file(&ra_fixture);
435 let parsed = db.parse(file_id);
436 let macro_calls: Vec<_> =
437 parsed.syntax_node().descendants().filter_map(ast::MacroCall::cast).collect();
438
439 let ast_id_map = db.ast_id_map(file_id.into());
440
441 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap();
442
443 let krate = CrateId(0);
444 let file_id = match expander {
445 Either::Left(expander) => {
446 // the first one should be a macro_rules
447 let def = MacroDefId {
448 krate: Some(CrateId(0)),
449 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))),
450 kind: MacroDefKind::BuiltIn(expander),
451 local_inner: false,
452 };
453
454 let loc = MacroCallLoc {
455 def,
456 krate,
457 kind: MacroCallKind::FnLike(AstId::new(
458 file_id.into(),
459 ast_id_map.ast_id(&macro_calls[1]),
460 )),
461 };
462
463 let id: MacroCallId = db.intern_macro(loc).into();
464 id.as_file()
465 }
466 Either::Right(expander) => {
467 // the first one should be a macro_rules
468 let def = MacroDefId {
469 krate: Some(krate),
470 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))),
471 kind: MacroDefKind::BuiltInEager(expander),
472 local_inner: false,
473 };
474
475 let args = macro_calls[1].token_tree().unwrap();
476 let parsed_args = mbe::ast_to_token_tree(&args).unwrap().0;
477
478 let arg_id = db.intern_eager_expansion({
479 EagerCallLoc {
480 def,
481 fragment: FragmentKind::Expr,
482 subtree: Arc::new(parsed_args.clone()),
483 krate,
484 file_id: file_id.into(),
485 }
486 });
487
488 let (subtree, fragment) = expander.expand(&db, arg_id, &parsed_args).unwrap();
489 let eager = EagerCallLoc {
490 def,
491 fragment,
492 subtree: Arc::new(subtree),
493 krate,
494 file_id: file_id.into(),
495 };
496
497 let id: MacroCallId = db.intern_eager_expansion(eager).into();
498 id.as_file()
499 }
500 };
501
502 db.parse_or_expand(file_id).unwrap().to_string()
503 }
504
505 #[test]
506 fn test_column_expand() {
507 let expanded = expand_builtin_macro(
508 r#"
509 #[rustc_builtin_macro]
510 macro_rules! column {() => {}}
511 column!()
512 "#,
513 );
514
515 assert_eq!(expanded, "0");
516 }
517
518 #[test]
519 fn test_line_expand() {
520 let expanded = expand_builtin_macro(
521 r#"
522 #[rustc_builtin_macro]
523 macro_rules! line {() => {}}
524 line!()
525 "#,
526 );
527
528 assert_eq!(expanded, "0");
529 }
530
531 #[test]
532 fn test_stringify_expand() {
533 let expanded = expand_builtin_macro(
534 r#"
535 #[rustc_builtin_macro]
536 macro_rules! stringify {() => {}}
537 stringify!(a b c)
538 "#,
539 );
540
541 assert_eq!(expanded, "\"a b c\"");
542 }
543
544 #[test]
545 fn test_env_expand() {
546 let expanded = expand_builtin_macro(
547 r#"
548 #[rustc_builtin_macro]
549 macro_rules! env {() => {}}
550 env!("TEST_ENV_VAR")
551 "#,
552 );
553
554 assert_eq!(expanded, "\"__RA_UNIMPLEMENTED__\"");
555 }
556
557 #[test]
558 fn test_option_env_expand() {
559 let expanded = expand_builtin_macro(
560 r#"
561 #[rustc_builtin_macro]
562 macro_rules! option_env {() => {}}
563 option_env!("TEST_ENV_VAR")
564 "#,
565 );
566
567 assert_eq!(expanded, "std::option::Option::None:: < &str>");
568 }
569
570 #[test]
571 fn test_file_expand() {
572 let expanded = expand_builtin_macro(
573 r#"
574 #[rustc_builtin_macro]
575 macro_rules! file {() => {}}
576 file!()
577 "#,
578 );
579
580 assert_eq!(expanded, "\"\"");
581 }
582
583 #[test]
584 fn test_assert_expand() {
585 let expanded = expand_builtin_macro(
586 r#"
587 #[rustc_builtin_macro]
588 macro_rules! assert {
589 ($cond:expr) => ({ /* compiler built-in */ });
590 ($cond:expr, $($args:tt)*) => ({ /* compiler built-in */ })
591 }
592 assert!(true, "{} {:?}", arg1(a, b, c), arg2);
593 "#,
594 );
595
596 assert_eq!(expanded, "{{(&(true), &(\"{} {:?}\"), &(arg1(a,b,c)), &(arg2),);}}");
597 }
598
599 #[test]
600 fn test_compile_error_expand() {
601 let expanded = expand_builtin_macro(
602 r#"
603 #[rustc_builtin_macro]
604 macro_rules! compile_error {
605 ($msg:expr) => ({ /* compiler built-in */ });
606 ($msg:expr,) => ({ /* compiler built-in */ })
607 }
608 compile_error!("error!");
609 "#,
610 );
611
612 assert_eq!(expanded, r#"loop{"error!"}"#);
613 }
614
615 #[test]
616 fn test_format_args_expand() {
617 let expanded = expand_builtin_macro(
618 r#"
619 #[rustc_builtin_macro]
620 macro_rules! format_args {
621 ($fmt:expr) => ({ /* compiler built-in */ });
622 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
623 }
624 format_args!("{} {:?}", arg1(a, b, c), arg2);
625 "#,
626 );
627
628 assert_eq!(
629 expanded,
630 r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a,b,c)),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(arg2),std::fmt::Display::fmt),])"#
631 );
632 }
633
634 #[test]
635 fn test_include_bytes_expand() {
636 let expanded = expand_builtin_macro(
637 r#"
638 #[rustc_builtin_macro]
639 macro_rules! include_bytes {
640 ($file:expr) => {{ /* compiler built-in */ }};
641 ($file:expr,) => {{ /* compiler built-in */ }};
642 }
643 include_bytes("foo");
644 "#,
645 );
646
647 assert_eq!(expanded, r#"b"""#);
648 }
649}
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
deleted file mode 100644
index f3b7cd492..000000000
--- a/crates/ra_hir_expand/src/db.rs
+++ /dev/null
@@ -1,404 +0,0 @@
1//! Defines database & queries for macro expansion.
2
3use std::sync::Arc;
4
5use mbe::{ExpandResult, MacroRules};
6use ra_db::{salsa, SourceDatabase};
7use ra_parser::FragmentKind;
8use ra_prof::profile;
9use ra_syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode};
10
11use crate::{
12 ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId,
13 HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind,
14 MacroFile, ProcMacroExpander,
15};
16
17#[derive(Debug, Clone, Eq, PartialEq)]
18pub enum TokenExpander {
19 MacroRules(mbe::MacroRules),
20 Builtin(BuiltinFnLikeExpander),
21 BuiltinDerive(BuiltinDeriveExpander),
22 ProcMacro(ProcMacroExpander),
23}
24
25impl TokenExpander {
26 pub fn expand(
27 &self,
28 db: &dyn AstDatabase,
29 id: LazyMacroId,
30 tt: &tt::Subtree,
31 ) -> mbe::ExpandResult<tt::Subtree> {
32 match self {
33 TokenExpander::MacroRules(it) => it.expand(tt),
34 // FIXME switch these to ExpandResult as well
35 TokenExpander::Builtin(it) => it.expand(db, id, tt).into(),
36 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
37 TokenExpander::ProcMacro(_) => {
38 // We store the result in salsa db to prevent non-determinisc behavior in
39 // some proc-macro implementation
40 // See #4315 for details
41 db.expand_proc_macro(id.into()).into()
42 }
43 }
44 }
45
46 pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
47 match self {
48 TokenExpander::MacroRules(it) => it.map_id_down(id),
49 TokenExpander::Builtin(..) => id,
50 TokenExpander::BuiltinDerive(..) => id,
51 TokenExpander::ProcMacro(..) => id,
52 }
53 }
54
55 pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) {
56 match self {
57 TokenExpander::MacroRules(it) => it.map_id_up(id),
58 TokenExpander::Builtin(..) => (id, mbe::Origin::Call),
59 TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call),
60 TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
61 }
62 }
63}
64
65// FIXME: rename to ExpandDatabase
66#[salsa::query_group(AstDatabaseStorage)]
67pub trait AstDatabase: SourceDatabase {
68 fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
69
70 #[salsa::transparent]
71 fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
72
73 #[salsa::interned]
74 fn intern_macro(&self, macro_call: MacroCallLoc) -> LazyMacroId;
75 fn macro_arg_text(&self, id: MacroCallId) -> Option<GreenNode>;
76 #[salsa::transparent]
77 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
78 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>;
79 fn parse_macro(&self, macro_file: MacroFile)
80 -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>;
81 fn macro_expand(&self, macro_call: MacroCallId) -> (Option<Arc<tt::Subtree>>, Option<String>);
82
83 #[salsa::interned]
84 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
85
86 fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>;
87}
88
89/// This expands the given macro call, but with different arguments. This is
90/// used for completion, where we want to see what 'would happen' if we insert a
91/// token. The `token_to_map` mapped down into the expansion, with the mapped
92/// token returned.
93pub fn expand_hypothetical(
94 db: &dyn AstDatabase,
95 actual_macro_call: MacroCallId,
96 hypothetical_args: &ra_syntax::ast::TokenTree,
97 token_to_map: ra_syntax::SyntaxToken,
98) -> Option<(SyntaxNode, ra_syntax::SyntaxToken)> {
99 let macro_file = MacroFile { macro_call_id: actual_macro_call };
100 let (tt, tmap_1) = mbe::syntax_node_to_token_tree(hypothetical_args.syntax()).unwrap();
101 let range =
102 token_to_map.text_range().checked_sub(hypothetical_args.syntax().text_range().start())?;
103 let token_id = tmap_1.token_by_range(range)?;
104 let macro_def = expander(db, actual_macro_call)?;
105 let (node, tmap_2) =
106 parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1))))?;
107 let token_id = macro_def.0.map_id_down(token_id);
108 let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?;
109 let token = ra_syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?;
110 Some((node.syntax_node(), token))
111}
112
113pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
114 let map =
115 db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it));
116 Arc::new(map)
117}
118
119pub(crate) fn macro_def(
120 db: &dyn AstDatabase,
121 id: MacroDefId,
122) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
123 match id.kind {
124 MacroDefKind::Declarative => {
125 let macro_call = id.ast_id?.to_node(db);
126 let arg = macro_call.token_tree()?;
127 let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
128 log::warn!("fail on macro_def to token tree: {:#?}", arg);
129 None
130 })?;
131 let rules = match MacroRules::parse(&tt) {
132 Ok(it) => it,
133 Err(err) => {
134 log::warn!("fail on macro_def parse: error: {:#?} {:#?}", err, tt);
135 return None;
136 }
137 };
138 Some(Arc::new((TokenExpander::MacroRules(rules), tmap)))
139 }
140 MacroDefKind::BuiltIn(expander) => {
141 Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default())))
142 }
143 MacroDefKind::BuiltInDerive(expander) => {
144 Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default())))
145 }
146 MacroDefKind::BuiltInEager(_) => None,
147 MacroDefKind::CustomDerive(expander) => {
148 Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default())))
149 }
150 }
151}
152
153pub(crate) fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
154 let id = match id {
155 MacroCallId::LazyMacro(id) => id,
156 MacroCallId::EagerMacro(_id) => {
157 // FIXME: support macro_arg for eager macro
158 return None;
159 }
160 };
161 let loc = db.lookup_intern_macro(id);
162 let arg = loc.kind.arg(db)?;
163 Some(arg.green().clone())
164}
165
166pub(crate) fn macro_arg(
167 db: &dyn AstDatabase,
168 id: MacroCallId,
169) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
170 let arg = db.macro_arg_text(id)?;
171 let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg))?;
172 Some(Arc::new((tt, tmap)))
173}
174
175pub(crate) fn macro_expand(
176 db: &dyn AstDatabase,
177 id: MacroCallId,
178) -> (Option<Arc<tt::Subtree>>, Option<String>) {
179 macro_expand_with_arg(db, id, None)
180}
181
182fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
183 let lazy_id = match id {
184 MacroCallId::LazyMacro(id) => id,
185 MacroCallId::EagerMacro(_id) => {
186 return None;
187 }
188 };
189
190 let loc = db.lookup_intern_macro(lazy_id);
191 let macro_rules = db.macro_def(loc.def)?;
192 Some(macro_rules)
193}
194
195fn macro_expand_with_arg(
196 db: &dyn AstDatabase,
197 id: MacroCallId,
198 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
199) -> (Option<Arc<tt::Subtree>>, Option<String>) {
200 let lazy_id = match id {
201 MacroCallId::LazyMacro(id) => id,
202 MacroCallId::EagerMacro(id) => {
203 if arg.is_some() {
204 return (
205 None,
206 Some("hypothetical macro expansion not implemented for eager macro".to_owned()),
207 );
208 } else {
209 return (Some(db.lookup_intern_eager_expansion(id).subtree), None);
210 }
211 }
212 };
213
214 let loc = db.lookup_intern_macro(lazy_id);
215 let macro_arg = match arg.or_else(|| db.macro_arg(id)) {
216 Some(it) => it,
217 None => return (None, Some("Fail to args in to tt::TokenTree".into())),
218 };
219
220 let macro_rules = match db.macro_def(loc.def) {
221 Some(it) => it,
222 None => return (None, Some("Fail to find macro definition".into())),
223 };
224 let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, &macro_arg.0);
225 // Set a hard limit for the expanded tt
226 let count = tt.count();
227 if count > 65536 {
228 return (None, Some(format!("Total tokens count exceed limit : count = {}", count)));
229 }
230 (Some(Arc::new(tt)), err.map(|e| format!("{:?}", e)))
231}
232
233pub(crate) fn expand_proc_macro(
234 db: &dyn AstDatabase,
235 id: MacroCallId,
236) -> Result<tt::Subtree, mbe::ExpandError> {
237 let lazy_id = match id {
238 MacroCallId::LazyMacro(id) => id,
239 MacroCallId::EagerMacro(_) => unreachable!(),
240 };
241
242 let loc = db.lookup_intern_macro(lazy_id);
243 let macro_arg = match db.macro_arg(id) {
244 Some(it) => it,
245 None => {
246 return Err(
247 tt::ExpansionError::Unknown("No arguments for proc-macro".to_string()).into()
248 )
249 }
250 };
251
252 let expander = match loc.def.kind {
253 MacroDefKind::CustomDerive(expander) => expander,
254 _ => unreachable!(),
255 };
256
257 expander.expand(db, lazy_id, &macro_arg.0)
258}
259
260pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
261 match file_id.0 {
262 HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
263 HirFileIdRepr::MacroFile(macro_file) => {
264 db.parse_macro(macro_file).map(|(it, _)| it.syntax_node())
265 }
266 }
267}
268
269pub(crate) fn parse_macro(
270 db: &dyn AstDatabase,
271 macro_file: MacroFile,
272) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
273 parse_macro_with_arg(db, macro_file, None)
274}
275
276pub fn parse_macro_with_arg(
277 db: &dyn AstDatabase,
278 macro_file: MacroFile,
279 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
280) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
281 let _p = profile("parse_macro_query");
282
283 let macro_call_id = macro_file.macro_call_id;
284 let (tt, err) = if let Some(arg) = arg {
285 macro_expand_with_arg(db, macro_call_id, Some(arg))
286 } else {
287 db.macro_expand(macro_call_id)
288 };
289 if let Some(err) = &err {
290 // Note:
291 // The final goal we would like to make all parse_macro success,
292 // such that the following log will not call anyway.
293 match macro_call_id {
294 MacroCallId::LazyMacro(id) => {
295 let loc: MacroCallLoc = db.lookup_intern_macro(id);
296 let node = loc.kind.node(db);
297
298 // collect parent information for warning log
299 let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| {
300 it.file_id.call_node(db)
301 })
302 .map(|n| format!("{:#}", n.value))
303 .collect::<Vec<_>>()
304 .join("\n");
305
306 log::warn!(
307 "fail on macro_parse: (reason: {} macro_call: {:#}) parents: {}",
308 err,
309 node.value,
310 parents
311 );
312 }
313 _ => {
314 log::warn!("fail on macro_parse: (reason: {})", err);
315 }
316 }
317 };
318 let tt = tt?;
319
320 let fragment_kind = to_fragment_kind(db, macro_call_id);
321
322 let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?;
323
324 if err.is_none() {
325 Some((parse, Arc::new(rev_token_map)))
326 } else {
327 // FIXME:
328 // In future, we should propagate the actual error with recovery information
329 // instead of ignore the error here.
330
331 // Safe check for recurisve identity macro
332 let node = parse.syntax_node();
333 let file: HirFileId = macro_file.into();
334 let call_node = file.call_node(db)?;
335
336 if !diff(&node, &call_node.value).is_empty() {
337 Some((parse, Arc::new(rev_token_map)))
338 } else {
339 None
340 }
341 }
342}
343
344/// Given a `MacroCallId`, return what `FragmentKind` it belongs to.
345/// FIXME: Not completed
346fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
347 let lazy_id = match id {
348 MacroCallId::LazyMacro(id) => id,
349 MacroCallId::EagerMacro(id) => {
350 return db.lookup_intern_eager_expansion(id).fragment;
351 }
352 };
353 let syn = db.lookup_intern_macro(lazy_id).kind.node(db).value;
354
355 let parent = match syn.parent() {
356 Some(it) => it,
357 None => {
358 // FIXME:
359 // If it is root, which means the parent HirFile
360 // MacroKindFile must be non-items
361 // return expr now.
362 return FragmentKind::Expr;
363 }
364 };
365
366 match parent.kind() {
367 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
368 ITEM_LIST => FragmentKind::Items,
369 LET_STMT => {
370 // FIXME: Handle Pattern
371 FragmentKind::Expr
372 }
373 // FIXME: Expand to statements in appropriate positions; HIR lowering needs to handle that
374 EXPR_STMT | BLOCK_EXPR => FragmentKind::Expr,
375 ARG_LIST => FragmentKind::Expr,
376 TRY_EXPR => FragmentKind::Expr,
377 TUPLE_EXPR => FragmentKind::Expr,
378 PAREN_EXPR => FragmentKind::Expr,
379
380 FOR_EXPR => FragmentKind::Expr,
381 PATH_EXPR => FragmentKind::Expr,
382 CLOSURE_EXPR => FragmentKind::Expr,
383 CONDITION => FragmentKind::Expr,
384 BREAK_EXPR => FragmentKind::Expr,
385 RETURN_EXPR => FragmentKind::Expr,
386 MATCH_EXPR => FragmentKind::Expr,
387 MATCH_ARM => FragmentKind::Expr,
388 MATCH_GUARD => FragmentKind::Expr,
389 RECORD_EXPR_FIELD => FragmentKind::Expr,
390 CALL_EXPR => FragmentKind::Expr,
391 INDEX_EXPR => FragmentKind::Expr,
392 METHOD_CALL_EXPR => FragmentKind::Expr,
393 AWAIT_EXPR => FragmentKind::Expr,
394 CAST_EXPR => FragmentKind::Expr,
395 REF_EXPR => FragmentKind::Expr,
396 PREFIX_EXPR => FragmentKind::Expr,
397 RANGE_EXPR => FragmentKind::Expr,
398 BIN_EXPR => FragmentKind::Expr,
399 _ => {
400 // Unknown , Just guess it is `Items`
401 FragmentKind::Items
402 }
403 }
404}
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
deleted file mode 100644
index 84ba97b14..000000000
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ /dev/null
@@ -1,110 +0,0 @@
1//! Semantic errors and warnings.
2//!
3//! The `Diagnostic` trait defines a trait object which can represent any
4//! diagnostic.
5//!
6//! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating
7//! a `DiagnosticSink`, you supply a callback which can react to a `dyn
8//! Diagnostic` or to any concrete diagnostic (downcasting is sued internally).
9//!
10//! Because diagnostics store file offsets, it's a bad idea to store them
11//! directly in salsa. For this reason, every hir subsytem defines it's own
12//! strongly-typed closed set of diagnostics which use hir ids internally, are
13//! stored in salsa and do *not* implement the `Diagnostic` trait. Instead, a
14//! subsystem provides a separate, non-query-based API which can walk all stored
15//! values and transform them into instances of `Diagnostic`.
16
17use std::{any::Any, fmt};
18
19use ra_syntax::{SyntaxNode, SyntaxNodePtr};
20
21use crate::{db::AstDatabase, InFile};
22
23pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
24 fn message(&self) -> String;
25 fn source(&self) -> InFile<SyntaxNodePtr>;
26 fn as_any(&self) -> &(dyn Any + Send + 'static);
27 fn is_experimental(&self) -> bool {
28 false
29 }
30}
31
32pub trait AstDiagnostic {
33 type AST;
34 fn ast(&self, db: &dyn AstDatabase) -> Self::AST;
35}
36
37impl dyn Diagnostic {
38 pub fn syntax_node(&self, db: &impl AstDatabase) -> SyntaxNode {
39 let node = db.parse_or_expand(self.source().file_id).unwrap();
40 self.source().value.to_node(&node)
41 }
42
43 pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> {
44 self.as_any().downcast_ref()
45 }
46}
47
48pub struct DiagnosticSink<'a> {
49 callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
50 filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>,
51 default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>,
52}
53
54impl<'a> DiagnosticSink<'a> {
55 pub fn push(&mut self, d: impl Diagnostic) {
56 let d: &dyn Diagnostic = &d;
57 self._push(d);
58 }
59
60 fn _push(&mut self, d: &dyn Diagnostic) {
61 for filter in &mut self.filters {
62 if !filter(d) {
63 return;
64 }
65 }
66 for cb in &mut self.callbacks {
67 match cb(d) {
68 Ok(()) => return,
69 Err(()) => (),
70 }
71 }
72 (self.default_callback)(d)
73 }
74}
75
76pub struct DiagnosticSinkBuilder<'a> {
77 callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
78 filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>,
79}
80
81impl<'a> DiagnosticSinkBuilder<'a> {
82 pub fn new() -> Self {
83 Self { callbacks: Vec::new(), filters: Vec::new() }
84 }
85
86 pub fn filter<F: FnMut(&dyn Diagnostic) -> bool + 'a>(mut self, cb: F) -> Self {
87 self.filters.push(Box::new(cb));
88 self
89 }
90
91 pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> Self {
92 let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::<D>() {
93 Some(d) => {
94 cb(d);
95 Ok(())
96 }
97 None => Err(()),
98 };
99 self.callbacks.push(Box::new(cb));
100 self
101 }
102
103 pub fn build<F: FnMut(&dyn Diagnostic) + 'a>(self, default_callback: F) -> DiagnosticSink<'a> {
104 DiagnosticSink {
105 callbacks: self.callbacks,
106 filters: self.filters,
107 default_callback: Box::new(default_callback),
108 }
109 }
110}
diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs
deleted file mode 100644
index 302d2b3e0..000000000
--- a/crates/ra_hir_expand/src/eager.rs
+++ /dev/null
@@ -1,144 +0,0 @@
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_db::CrateId;
29use ra_parser::FragmentKind;
30use ra_syntax::{algo::SyntaxRewriter, SyntaxNode};
31use std::sync::Arc;
32
33pub fn expand_eager_macro(
34 db: &dyn AstDatabase,
35 krate: CrateId,
36 macro_call: InFile<ast::MacroCall>,
37 def: MacroDefId,
38 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
39) -> Option<EagerMacroId> {
40 let args = macro_call.value.token_tree()?;
41 let parsed_args = mbe::ast_to_token_tree(&args)?.0;
42
43 // Note:
44 // When `lazy_expand` is called, its *parent* file must be already exists.
45 // Here we store an eager macro id for the argument expanded subtree here
46 // for that purpose.
47 let arg_id = db.intern_eager_expansion({
48 EagerCallLoc {
49 def,
50 fragment: FragmentKind::Expr,
51 subtree: Arc::new(parsed_args.clone()),
52 krate,
53 file_id: macro_call.file_id,
54 }
55 });
56 let arg_file_id: MacroCallId = arg_id.into();
57
58 let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0;
59 let result = eager_macro_recur(
60 db,
61 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()),
62 krate,
63 resolver,
64 )?;
65 let subtree = to_subtree(&result)?;
66
67 if let MacroDefKind::BuiltInEager(eager) = def.kind {
68 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?;
69 let eager = EagerCallLoc {
70 def,
71 fragment,
72 subtree: Arc::new(subtree),
73 krate,
74 file_id: macro_call.file_id,
75 };
76
77 Some(db.intern_eager_expansion(eager))
78 } else {
79 None
80 }
81}
82
83fn to_subtree(node: &SyntaxNode) -> Option<tt::Subtree> {
84 let mut subtree = mbe::syntax_node_to_token_tree(node)?.0;
85 subtree.delimiter = None;
86 Some(subtree)
87}
88
89fn lazy_expand(
90 db: &dyn AstDatabase,
91 def: &MacroDefId,
92 macro_call: InFile<ast::MacroCall>,
93 krate: CrateId,
94) -> Option<InFile<SyntaxNode>> {
95 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
96
97 let id: MacroCallId =
98 def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
99
100 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node))
101}
102
103fn eager_macro_recur(
104 db: &dyn AstDatabase,
105 curr: InFile<SyntaxNode>,
106 krate: CrateId,
107 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
108) -> Option<SyntaxNode> {
109 let original = curr.value.clone();
110
111 let children = curr.value.descendants().filter_map(ast::MacroCall::cast);
112 let mut rewriter = SyntaxRewriter::default();
113
114 // Collect replacement
115 for child in children {
116 let def: MacroDefId = macro_resolver(child.path()?)?;
117 let insert = match def.kind {
118 MacroDefKind::BuiltInEager(_) => {
119 let id: MacroCallId = expand_eager_macro(
120 db,
121 krate,
122 curr.with_value(child.clone()),
123 def,
124 macro_resolver,
125 )?
126 .into();
127 db.parse_or_expand(id.as_file())?
128 }
129 MacroDefKind::Declarative
130 | MacroDefKind::BuiltIn(_)
131 | MacroDefKind::BuiltInDerive(_)
132 | MacroDefKind::CustomDerive(_) => {
133 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?;
134 // replace macro inside
135 eager_macro_recur(db, expanded, krate, macro_resolver)?
136 }
137 };
138
139 rewriter.replace(child.syntax(), &insert);
140 }
141
142 let res = rewriter.rewrite(&original);
143 Some(res)
144}
diff --git a/crates/ra_hir_expand/src/hygiene.rs b/crates/ra_hir_expand/src/hygiene.rs
deleted file mode 100644
index 6b482a60c..000000000
--- a/crates/ra_hir_expand/src/hygiene.rs
+++ /dev/null
@@ -1,66 +0,0 @@
1//! This modules handles hygiene information.
2//!
3//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
4//! this moment, this is horribly incomplete and handles only `$crate`.
5use either::Either;
6use ra_db::CrateId;
7use ra_syntax::ast;
8
9use crate::{
10 db::AstDatabase,
11 name::{AsName, Name},
12 HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind,
13};
14
15#[derive(Clone, Debug)]
16pub struct Hygiene {
17 // This is what `$crate` expands to
18 def_crate: Option<CrateId>,
19
20 // Indiciate this is a local inner macro
21 local_inner: bool,
22}
23
24impl Hygiene {
25 pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene {
26 let (def_crate, local_inner) = match file_id.0 {
27 HirFileIdRepr::FileId(_) => (None, false),
28 HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
29 MacroCallId::LazyMacro(id) => {
30 let loc = db.lookup_intern_macro(id);
31 match loc.def.kind {
32 MacroDefKind::Declarative => (loc.def.krate, loc.def.local_inner),
33 MacroDefKind::BuiltIn(_) => (None, false),
34 MacroDefKind::BuiltInDerive(_) => (None, false),
35 MacroDefKind::BuiltInEager(_) => (None, false),
36 MacroDefKind::CustomDerive(_) => (None, false),
37 }
38 }
39 MacroCallId::EagerMacro(_id) => (None, false),
40 },
41 };
42 Hygiene { def_crate, local_inner }
43 }
44
45 pub fn new_unhygienic() -> Hygiene {
46 Hygiene { def_crate: None, local_inner: false }
47 }
48
49 // FIXME: this should just return name
50 pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> {
51 if let Some(def_crate) = self.def_crate {
52 if name_ref.text() == "$crate" {
53 return Either::Right(def_crate);
54 }
55 }
56 Either::Left(name_ref.as_name())
57 }
58
59 pub fn local_inner_macros(&self) -> Option<CrateId> {
60 if self.local_inner {
61 self.def_crate
62 } else {
63 None
64 }
65 }
66}
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
deleted file mode 100644
index 2e8d63691..000000000
--- a/crates/ra_hir_expand/src/lib.rs
+++ /dev/null
@@ -1,452 +0,0 @@
1//! `ra_hir_expand` deals with macro expansion.
2//!
3//! Specifically, it implements a concept of `MacroFile` -- a file whose syntax
4//! tree originates not from the text of some `FileId`, but from some macro
5//! expansion.
6
7pub mod db;
8pub mod ast_id_map;
9pub mod name;
10pub mod hygiene;
11pub mod diagnostics;
12pub mod builtin_derive;
13pub mod builtin_macro;
14pub mod proc_macro;
15pub mod quote;
16pub mod eager;
17
18use std::hash::Hash;
19use std::sync::Arc;
20
21use ra_db::{impl_intern_key, salsa, CrateId, FileId};
22use ra_syntax::{
23 algo,
24 ast::{self, AstNode},
25 SyntaxNode, SyntaxToken, TextSize,
26};
27
28use crate::ast_id_map::FileAstId;
29use crate::builtin_derive::BuiltinDeriveExpander;
30use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander};
31use crate::proc_macro::ProcMacroExpander;
32
33#[cfg(test)]
34mod test_db;
35
36/// Input to the analyzer is a set of files, where each file is identified by
37/// `FileId` and contains source code. However, another source of source code in
38/// Rust are macros: each macro can be thought of as producing a "temporary
39/// file". To assign an id to such a file, we use the id of the macro call that
40/// produced the file. So, a `HirFileId` is either a `FileId` (source code
41/// written by user), or a `MacroCallId` (source code produced by macro).
42///
43/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file
44/// containing the call plus the offset of the macro call in the file. Note that
45/// this is a recursive definition! However, the size_of of `HirFileId` is
46/// finite (because everything bottoms out at the real `FileId`) and small
47/// (`MacroCallId` uses the location interner).
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49pub struct HirFileId(HirFileIdRepr);
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
52enum HirFileIdRepr {
53 FileId(FileId),
54 MacroFile(MacroFile),
55}
56
57impl From<FileId> for HirFileId {
58 fn from(id: FileId) -> Self {
59 HirFileId(HirFileIdRepr::FileId(id))
60 }
61}
62
63impl From<MacroFile> for HirFileId {
64 fn from(id: MacroFile) -> Self {
65 HirFileId(HirFileIdRepr::MacroFile(id))
66 }
67}
68
69impl HirFileId {
70 /// For macro-expansion files, returns the file original source file the
71 /// expansion originated from.
72 pub fn original_file(self, db: &dyn db::AstDatabase) -> FileId {
73 match self.0 {
74 HirFileIdRepr::FileId(file_id) => file_id,
75 HirFileIdRepr::MacroFile(macro_file) => {
76 let file_id = match macro_file.macro_call_id {
77 MacroCallId::LazyMacro(id) => {
78 let loc = db.lookup_intern_macro(id);
79 loc.kind.file_id()
80 }
81 MacroCallId::EagerMacro(id) => {
82 let loc = db.lookup_intern_eager_expansion(id);
83 loc.file_id
84 }
85 };
86 file_id.original_file(db)
87 }
88 }
89 }
90
91 pub fn expansion_level(self, db: &dyn db::AstDatabase) -> u32 {
92 let mut level = 0;
93 let mut curr = self;
94 while let HirFileIdRepr::MacroFile(macro_file) = curr.0 {
95 level += 1;
96 curr = match macro_file.macro_call_id {
97 MacroCallId::LazyMacro(id) => {
98 let loc = db.lookup_intern_macro(id);
99 loc.kind.file_id()
100 }
101 MacroCallId::EagerMacro(id) => {
102 let loc = db.lookup_intern_eager_expansion(id);
103 loc.file_id
104 }
105 };
106 }
107 level
108 }
109
110 /// If this is a macro call, returns the syntax node of the call.
111 pub fn call_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> {
112 match self.0 {
113 HirFileIdRepr::FileId(_) => None,
114 HirFileIdRepr::MacroFile(macro_file) => {
115 let lazy_id = match macro_file.macro_call_id {
116 MacroCallId::LazyMacro(id) => id,
117 MacroCallId::EagerMacro(_id) => {
118 // FIXME: handle call node for eager macro
119 return None;
120 }
121 };
122 let loc = db.lookup_intern_macro(lazy_id);
123 Some(loc.kind.node(db))
124 }
125 }
126 }
127
128 /// Return expansion information if it is a macro-expansion file
129 pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> {
130 match self.0 {
131 HirFileIdRepr::FileId(_) => None,
132 HirFileIdRepr::MacroFile(macro_file) => {
133 let lazy_id = match macro_file.macro_call_id {
134 MacroCallId::LazyMacro(id) => id,
135 MacroCallId::EagerMacro(_id) => {
136 // FIXME: handle expansion_info for eager macro
137 return None;
138 }
139 };
140 let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id);
141
142 let arg_tt = loc.kind.arg(db)?;
143 let def_tt = loc.def.ast_id?.to_node(db).token_tree()?;
144
145 let macro_def = db.macro_def(loc.def)?;
146 let (parse, exp_map) = db.parse_macro(macro_file)?;
147 let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
148
149 Some(ExpansionInfo {
150 expanded: InFile::new(self, parse.syntax_node()),
151 arg: InFile::new(loc.kind.file_id(), arg_tt),
152 def: InFile::new(loc.def.ast_id?.file_id, def_tt),
153 macro_arg,
154 macro_def,
155 exp_map,
156 })
157 }
158 }
159 }
160
161 /// Indicate it is macro file generated for builtin derive
162 pub fn is_builtin_derive(&self, db: &dyn db::AstDatabase) -> Option<InFile<ast::Item>> {
163 match self.0 {
164 HirFileIdRepr::FileId(_) => None,
165 HirFileIdRepr::MacroFile(macro_file) => {
166 let lazy_id = match macro_file.macro_call_id {
167 MacroCallId::LazyMacro(id) => id,
168 MacroCallId::EagerMacro(_id) => {
169 return None;
170 }
171 };
172 let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id);
173 let item = match loc.def.kind {
174 MacroDefKind::BuiltInDerive(_) => loc.kind.node(db),
175 _ => return None,
176 };
177 Some(item.with_value(ast::Item::cast(item.value.clone())?))
178 }
179 }
180 }
181}
182
183#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
184pub struct MacroFile {
185 macro_call_id: MacroCallId,
186}
187
188/// `MacroCallId` identifies a particular macro invocation, like
189/// `println!("Hello, {}", world)`.
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
191pub enum MacroCallId {
192 LazyMacro(LazyMacroId),
193 EagerMacro(EagerMacroId),
194}
195
196#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
197pub struct LazyMacroId(salsa::InternId);
198impl_intern_key!(LazyMacroId);
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
201pub struct EagerMacroId(salsa::InternId);
202impl_intern_key!(EagerMacroId);
203
204impl From<LazyMacroId> for MacroCallId {
205 fn from(it: LazyMacroId) -> Self {
206 MacroCallId::LazyMacro(it)
207 }
208}
209impl From<EagerMacroId> for MacroCallId {
210 fn from(it: EagerMacroId) -> Self {
211 MacroCallId::EagerMacro(it)
212 }
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
216pub struct MacroDefId {
217 // FIXME: krate and ast_id are currently optional because we don't have a
218 // definition location for built-in derives. There is one, though: the
219 // standard library defines them. The problem is that it uses the new
220 // `macro` syntax for this, which we don't support yet. As soon as we do
221 // (which will probably require touching this code), we can instead use
222 // that (and also remove the hacks for resolving built-in derives).
223 pub krate: Option<CrateId>,
224 pub ast_id: Option<AstId<ast::MacroCall>>,
225 pub kind: MacroDefKind,
226
227 pub local_inner: bool,
228}
229
230impl MacroDefId {
231 pub fn as_lazy_macro(
232 self,
233 db: &dyn db::AstDatabase,
234 krate: CrateId,
235 kind: MacroCallKind,
236 ) -> LazyMacroId {
237 db.intern_macro(MacroCallLoc { def: self, krate, kind })
238 }
239}
240
241#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
242pub enum MacroDefKind {
243 Declarative,
244 BuiltIn(BuiltinFnLikeExpander),
245 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander
246 BuiltInDerive(BuiltinDeriveExpander),
247 BuiltInEager(EagerExpander),
248 CustomDerive(ProcMacroExpander),
249}
250
251#[derive(Debug, Clone, PartialEq, Eq, Hash)]
252pub struct MacroCallLoc {
253 pub(crate) def: MacroDefId,
254 pub(crate) krate: CrateId,
255 pub(crate) kind: MacroCallKind,
256}
257
258#[derive(Debug, Clone, PartialEq, Eq, Hash)]
259pub enum MacroCallKind {
260 FnLike(AstId<ast::MacroCall>),
261 Attr(AstId<ast::Item>, String),
262}
263
264impl MacroCallKind {
265 fn file_id(&self) -> HirFileId {
266 match self {
267 MacroCallKind::FnLike(ast_id) => ast_id.file_id,
268 MacroCallKind::Attr(ast_id, _) => ast_id.file_id,
269 }
270 }
271
272 fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> {
273 match self {
274 MacroCallKind::FnLike(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()),
275 MacroCallKind::Attr(ast_id, _) => {
276 ast_id.with_value(ast_id.to_node(db).syntax().clone())
277 }
278 }
279 }
280
281 fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> {
282 match self {
283 MacroCallKind::FnLike(ast_id) => {
284 Some(ast_id.to_node(db).token_tree()?.syntax().clone())
285 }
286 MacroCallKind::Attr(ast_id, _) => Some(ast_id.to_node(db).syntax().clone()),
287 }
288 }
289}
290
291impl MacroCallId {
292 pub fn as_file(self) -> HirFileId {
293 MacroFile { macro_call_id: self }.into()
294 }
295}
296
297#[derive(Debug, Clone, PartialEq, Eq, Hash)]
298pub struct EagerCallLoc {
299 pub(crate) def: MacroDefId,
300 pub(crate) fragment: FragmentKind,
301 pub(crate) subtree: Arc<tt::Subtree>,
302 pub(crate) krate: CrateId,
303 pub(crate) file_id: HirFileId,
304}
305
306/// ExpansionInfo mainly describes how to map text range between src and expanded macro
307#[derive(Debug, Clone, PartialEq, Eq)]
308pub struct ExpansionInfo {
309 expanded: InFile<SyntaxNode>,
310 arg: InFile<SyntaxNode>,
311 def: InFile<ast::TokenTree>,
312
313 macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>,
314 macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
315 exp_map: Arc<mbe::TokenMap>,
316}
317
318pub use mbe::Origin;
319use ra_parser::FragmentKind;
320
321impl ExpansionInfo {
322 pub fn call_node(&self) -> Option<InFile<SyntaxNode>> {
323 Some(self.arg.with_value(self.arg.value.parent()?))
324 }
325
326 pub fn map_token_down(&self, token: InFile<&SyntaxToken>) -> Option<InFile<SyntaxToken>> {
327 assert_eq!(token.file_id, self.arg.file_id);
328 let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
329 let token_id = self.macro_arg.1.token_by_range(range)?;
330 let token_id = self.macro_def.0.map_id_down(token_id);
331
332 let range = self.exp_map.range_by_token(token_id)?.by_kind(token.value.kind())?;
333
334 let token = algo::find_covering_element(&self.expanded.value, range).into_token()?;
335
336 Some(self.expanded.with_value(token))
337 }
338
339 pub fn map_token_up(
340 &self,
341 token: InFile<&SyntaxToken>,
342 ) -> Option<(InFile<SyntaxToken>, Origin)> {
343 let token_id = self.exp_map.token_by_range(token.value.text_range())?;
344
345 let (token_id, origin) = self.macro_def.0.map_id_up(token_id);
346 let (token_map, tt) = match origin {
347 mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()),
348 mbe::Origin::Def => {
349 (&self.macro_def.1, self.def.as_ref().map(|tt| tt.syntax().clone()))
350 }
351 };
352
353 let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?;
354 let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start())
355 .into_token()?;
356 Some((tt.with_value(token), origin))
357 }
358}
359
360/// `AstId` points to an AST node in any file.
361///
362/// It is stable across reparses, and can be used as salsa key/value.
363// FIXME: isn't this just a `Source<FileAstId<N>>` ?
364pub type AstId<N> = InFile<FileAstId<N>>;
365
366impl<N: AstNode> AstId<N> {
367 pub fn to_node(&self, db: &dyn db::AstDatabase) -> N {
368 let root = db.parse_or_expand(self.file_id).unwrap();
369 db.ast_id_map(self.file_id).get(self.value).to_node(&root)
370 }
371}
372
373/// `InFile<T>` stores a value of `T` inside a particular file/syntax tree.
374///
375/// Typical usages are:
376///
377/// * `InFile<SyntaxNode>` -- syntax node in a file
378/// * `InFile<ast::FnDef>` -- ast node in a file
379/// * `InFile<TextSize>` -- offset in a file
380#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
381pub struct InFile<T> {
382 pub file_id: HirFileId,
383 pub value: T,
384}
385
386impl<T> InFile<T> {
387 pub fn new(file_id: HirFileId, value: T) -> InFile<T> {
388 InFile { file_id, value }
389 }
390
391 // Similarly, naming here is stupid...
392 pub fn with_value<U>(&self, value: U) -> InFile<U> {
393 InFile::new(self.file_id, value)
394 }
395
396 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> InFile<U> {
397 InFile::new(self.file_id, f(self.value))
398 }
399 pub fn as_ref(&self) -> InFile<&T> {
400 self.with_value(&self.value)
401 }
402 pub fn file_syntax(&self, db: &dyn db::AstDatabase) -> SyntaxNode {
403 db.parse_or_expand(self.file_id).expect("source created from invalid file")
404 }
405}
406
407impl<T: Clone> InFile<&T> {
408 pub fn cloned(&self) -> InFile<T> {
409 self.with_value(self.value.clone())
410 }
411}
412
413impl<T> InFile<Option<T>> {
414 pub fn transpose(self) -> Option<InFile<T>> {
415 let value = self.value?;
416 Some(InFile::new(self.file_id, value))
417 }
418}
419
420impl InFile<SyntaxNode> {
421 pub fn ancestors_with_macros(
422 self,
423 db: &dyn db::AstDatabase,
424 ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
425 std::iter::successors(Some(self), move |node| match node.value.parent() {
426 Some(parent) => Some(node.with_value(parent)),
427 None => {
428 let parent_node = node.file_id.call_node(db)?;
429 Some(parent_node)
430 }
431 })
432 }
433}
434
435impl InFile<SyntaxToken> {
436 pub fn ancestors_with_macros(
437 self,
438 db: &dyn db::AstDatabase,
439 ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
440 self.map(|it| it.parent()).ancestors_with_macros(db)
441 }
442}
443
444impl<N: AstNode> InFile<N> {
445 pub fn descendants<T: AstNode>(self) -> impl Iterator<Item = InFile<T>> {
446 self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n))
447 }
448
449 pub fn syntax(&self) -> InFile<&SyntaxNode> {
450 self.with_value(self.value.syntax())
451 }
452}
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
deleted file mode 100644
index 969a2e5b8..000000000
--- a/crates/ra_hir_expand/src/name.rs
+++ /dev/null
@@ -1,230 +0,0 @@
1//! FIXME: write short doc here
2
3use std::fmt;
4
5use ra_syntax::{ast, SmolStr};
6
7/// `Name` is a wrapper around string, which is used in hir for both references
8/// and declarations. In theory, names should also carry hygiene info, but we are
9/// not there yet!
10#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
11pub struct Name(Repr);
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
14enum Repr {
15 Text(SmolStr),
16 TupleField(usize),
17}
18
19impl fmt::Display for Name {
20 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21 match &self.0 {
22 Repr::Text(text) => fmt::Display::fmt(&text, f),
23 Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
24 }
25 }
26}
27
28impl Name {
29 /// Note: this is private to make creating name from random string hard.
30 /// Hopefully, this should allow us to integrate hygiene cleaner in the
31 /// future, and to switch to interned representation of names.
32 const fn new_text(text: SmolStr) -> Name {
33 Name(Repr::Text(text))
34 }
35
36 pub fn new_tuple_field(idx: usize) -> Name {
37 Name(Repr::TupleField(idx))
38 }
39
40 pub fn new_lifetime(lt: &ra_syntax::SyntaxToken) -> Name {
41 assert!(lt.kind() == ra_syntax::SyntaxKind::LIFETIME);
42 Name(Repr::Text(lt.text().clone()))
43 }
44
45 /// Shortcut to create inline plain text name
46 const fn new_inline_ascii(text: &[u8]) -> Name {
47 Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text))
48 }
49
50 /// Resolve a name from the text of token.
51 fn resolve(raw_text: &SmolStr) -> Name {
52 let raw_start = "r#";
53 if raw_text.as_str().starts_with(raw_start) {
54 Name::new_text(SmolStr::new(&raw_text[raw_start.len()..]))
55 } else {
56 Name::new_text(raw_text.clone())
57 }
58 }
59
60 pub fn missing() -> Name {
61 Name::new_text("[missing name]".into())
62 }
63
64 pub fn as_tuple_index(&self) -> Option<usize> {
65 match self.0 {
66 Repr::TupleField(idx) => Some(idx),
67 _ => None,
68 }
69 }
70}
71
72pub trait AsName {
73 fn as_name(&self) -> Name;
74}
75
76impl AsName for ast::NameRef {
77 fn as_name(&self) -> Name {
78 match self.as_tuple_field() {
79 Some(idx) => Name::new_tuple_field(idx),
80 None => Name::resolve(self.text()),
81 }
82 }
83}
84
85impl AsName for ast::Name {
86 fn as_name(&self) -> Name {
87 Name::resolve(self.text())
88 }
89}
90
91impl AsName for ast::NameOrNameRef {
92 fn as_name(&self) -> Name {
93 match self {
94 ast::NameOrNameRef::Name(it) => it.as_name(),
95 ast::NameOrNameRef::NameRef(it) => it.as_name(),
96 }
97 }
98}
99
100impl AsName for tt::Ident {
101 fn as_name(&self) -> Name {
102 Name::resolve(&self.text)
103 }
104}
105
106impl AsName for ast::FieldKind {
107 fn as_name(&self) -> Name {
108 match self {
109 ast::FieldKind::Name(nr) => nr.as_name(),
110 ast::FieldKind::Index(idx) => {
111 let idx = idx.text().parse::<usize>().unwrap_or(0);
112 Name::new_tuple_field(idx)
113 }
114 }
115 }
116}
117
118impl AsName for ra_db::Dependency {
119 fn as_name(&self) -> Name {
120 Name::new_text(SmolStr::new(&*self.name))
121 }
122}
123
124pub mod known {
125 macro_rules! known_names {
126 ($($ident:ident),* $(,)?) => {
127 $(
128 #[allow(bad_style)]
129 pub const $ident: super::Name =
130 super::Name::new_inline_ascii(stringify!($ident).as_bytes());
131 )*
132 };
133 }
134
135 known_names!(
136 // Primitives
137 isize,
138 i8,
139 i16,
140 i32,
141 i64,
142 i128,
143 usize,
144 u8,
145 u16,
146 u32,
147 u64,
148 u128,
149 f32,
150 f64,
151 bool,
152 char,
153 str,
154 // Special names
155 macro_rules,
156 doc,
157 // Components of known path (value or mod name)
158 std,
159 core,
160 alloc,
161 iter,
162 ops,
163 future,
164 result,
165 boxed,
166 // Components of known path (type name)
167 IntoIterator,
168 Item,
169 Try,
170 Ok,
171 Future,
172 Result,
173 Output,
174 Target,
175 Box,
176 RangeFrom,
177 RangeFull,
178 RangeInclusive,
179 RangeToInclusive,
180 RangeTo,
181 Range,
182 Neg,
183 Not,
184 Index,
185 // Builtin macros
186 file,
187 column,
188 compile_error,
189 line,
190 assert,
191 stringify,
192 concat,
193 include,
194 include_bytes,
195 include_str,
196 format_args,
197 format_args_nl,
198 env,
199 option_env,
200 // Builtin derives
201 Copy,
202 Clone,
203 Default,
204 Debug,
205 Hash,
206 Ord,
207 PartialOrd,
208 Eq,
209 PartialEq,
210 );
211
212 // self/Self cannot be used as an identifier
213 pub const SELF_PARAM: super::Name = super::Name::new_inline_ascii(b"self");
214 pub const SELF_TYPE: super::Name = super::Name::new_inline_ascii(b"Self");
215
216 #[macro_export]
217 macro_rules! name {
218 (self) => {
219 $crate::name::known::SELF_PARAM
220 };
221 (Self) => {
222 $crate::name::known::SELF_TYPE
223 };
224 ($ident:ident) => {
225 $crate::name::known::$ident
226 };
227 }
228}
229
230pub use crate::name;
diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs
deleted file mode 100644
index 2c0ec41d2..000000000
--- a/crates/ra_hir_expand/src/proc_macro.rs
+++ /dev/null
@@ -1,143 +0,0 @@
1//! Proc Macro Expander stub
2
3use crate::{db::AstDatabase, LazyMacroId};
4use ra_db::{CrateId, ProcMacroId};
5use tt::buffer::{Cursor, TokenBuffer};
6
7#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
8pub struct ProcMacroExpander {
9 krate: CrateId,
10 proc_macro_id: ProcMacroId,
11}
12
13macro_rules! err {
14 ($fmt:literal, $($tt:tt),*) => {
15 mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown(format!($fmt, $($tt),*)))
16 };
17 ($fmt:literal) => {
18 mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown($fmt.to_string()))
19 }
20}
21
22impl ProcMacroExpander {
23 pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander {
24 ProcMacroExpander { krate, proc_macro_id }
25 }
26
27 pub fn expand(
28 self,
29 db: &dyn AstDatabase,
30 _id: LazyMacroId,
31 tt: &tt::Subtree,
32 ) -> Result<tt::Subtree, mbe::ExpandError> {
33 let krate_graph = db.crate_graph();
34 let proc_macro = krate_graph[self.krate]
35 .proc_macro
36 .get(self.proc_macro_id.0 as usize)
37 .clone()
38 .ok_or_else(|| err!("No derive macro found."))?;
39
40 let tt = remove_derive_attrs(tt)
41 .ok_or_else(|| err!("Fail to remove derive for custom derive"))?;
42
43 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from)
44 }
45}
46
47fn eat_punct(cursor: &mut Cursor, c: char) -> bool {
48 if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = cursor.token_tree() {
49 if punct.char == c {
50 *cursor = cursor.bump();
51 return true;
52 }
53 }
54 false
55}
56
57fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool {
58 if let Some(tt::TokenTree::Subtree(subtree)) = cursor.token_tree() {
59 if Some(kind) == subtree.delimiter_kind() {
60 *cursor = cursor.bump_subtree();
61 return true;
62 }
63 }
64 false
65}
66
67fn eat_ident(cursor: &mut Cursor, t: &str) -> bool {
68 if let Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) = cursor.token_tree() {
69 if t == ident.text.as_str() {
70 *cursor = cursor.bump();
71 return true;
72 }
73 }
74 false
75}
76
77fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> {
78 let buffer = TokenBuffer::new(&tt.token_trees);
79 let mut p = buffer.begin();
80 let mut result = tt::Subtree::default();
81
82 while !p.eof() {
83 let curr = p;
84
85 if eat_punct(&mut p, '#') {
86 eat_punct(&mut p, '!');
87 let parent = p;
88 if eat_subtree(&mut p, tt::DelimiterKind::Bracket) {
89 if eat_ident(&mut p, "derive") {
90 p = parent.bump();
91 continue;
92 }
93 }
94 }
95
96 result.token_trees.push(curr.token_tree()?.clone());
97 p = curr.bump();
98 }
99
100 Some(result)
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use test_utils::assert_eq_text;
107
108 #[test]
109 fn test_remove_derive_attrs() {
110 let tt = mbe::parse_to_token_tree(
111 r#"
112 #[allow(unused)]
113 #[derive(Copy)]
114 #[derive(Hello)]
115 struct A {
116 bar: u32
117 }
118"#,
119 )
120 .unwrap()
121 .0;
122 let result = format!("{:#?}", remove_derive_attrs(&tt).unwrap());
123
124 assert_eq_text!(
125 &result,
126 r#"
127SUBTREE $
128 PUNCH # [alone] 0
129 SUBTREE [] 1
130 IDENT allow 2
131 SUBTREE () 3
132 IDENT unused 4
133 IDENT struct 15
134 IDENT A 16
135 SUBTREE {} 17
136 IDENT bar 18
137 PUNCH : [alone] 19
138 IDENT u32 20
139"#
140 .trim()
141 );
142 }
143}
diff --git a/crates/ra_hir_expand/src/quote.rs b/crates/ra_hir_expand/src/quote.rs
deleted file mode 100644
index 219bc2097..000000000
--- a/crates/ra_hir_expand/src/quote.rs
+++ /dev/null
@@ -1,282 +0,0 @@
1//! A simplified version of quote-crate like quasi quote macro
2
3// A helper macro quote macro
4// FIXME:
5// 1. Not all puncts are handled
6// 2. #()* pattern repetition not supported now
7// * But we can do it manually, see `test_quote_derive_copy_hack`
8#[doc(hidden)]
9#[macro_export]
10macro_rules! __quote {
11 () => {
12 Vec::<tt::TokenTree>::new()
13 };
14
15 ( @SUBTREE $delim:ident $($tt:tt)* ) => {
16 {
17 let children = $crate::__quote!($($tt)*);
18 tt::Subtree {
19 delimiter: Some(tt::Delimiter {
20 kind: tt::DelimiterKind::$delim,
21 id: tt::TokenId::unspecified(),
22 }),
23 token_trees: $crate::quote::IntoTt::to_tokens(children),
24 }
25 }
26 };
27
28 ( @PUNCT $first:literal ) => {
29 {
30 vec![
31 tt::Leaf::Punct(tt::Punct {
32 char: $first,
33 spacing: tt::Spacing::Alone,
34 id: tt::TokenId::unspecified(),
35 }).into()
36 ]
37 }
38 };
39
40 ( @PUNCT $first:literal, $sec:literal ) => {
41 {
42 vec![
43 tt::Leaf::Punct(tt::Punct {
44 char: $first,
45 spacing: tt::Spacing::Joint,
46 id: tt::TokenId::unspecified(),
47 }).into(),
48 tt::Leaf::Punct(tt::Punct {
49 char: $sec,
50 spacing: tt::Spacing::Alone,
51 id: tt::TokenId::unspecified(),
52 }).into()
53 ]
54 }
55 };
56
57 // hash variable
58 ( # $first:ident $($tail:tt)* ) => {
59 {
60 let token = $crate::quote::ToTokenTree::to_token($first);
61 let mut tokens = vec![token.into()];
62 let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
63 tokens.append(&mut tail_tokens);
64 tokens
65 }
66 };
67
68 ( ## $first:ident $($tail:tt)* ) => {
69 {
70 let mut tokens = $first.into_iter().map($crate::quote::ToTokenTree::to_token).collect::<Vec<tt::TokenTree>>();
71 let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
72 tokens.append(&mut tail_tokens);
73 tokens
74 }
75 };
76
77 // Brace
78 ( { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE Brace $($tt)*) };
79 // Bracket
80 ( [ $($tt:tt)* ] ) => { $crate::__quote!(@SUBTREE Bracket $($tt)*) };
81 // Parenthesis
82 ( ( $($tt:tt)* ) ) => { $crate::__quote!(@SUBTREE Parenthesis $($tt)*) };
83
84 // Literal
85 ( $tt:literal ) => { vec![$crate::quote::ToTokenTree::to_token($tt).into()] };
86 // Ident
87 ( $tt:ident ) => {
88 vec![ {
89 tt::Leaf::Ident(tt::Ident {
90 text: stringify!($tt).into(),
91 id: tt::TokenId::unspecified(),
92 }).into()
93 }]
94 };
95
96 // Puncts
97 // FIXME: Not all puncts are handled
98 ( -> ) => {$crate::__quote!(@PUNCT '-', '>')};
99 ( & ) => {$crate::__quote!(@PUNCT '&')};
100 ( , ) => {$crate::__quote!(@PUNCT ',')};
101 ( : ) => {$crate::__quote!(@PUNCT ':')};
102 ( ; ) => {$crate::__quote!(@PUNCT ';')};
103 ( :: ) => {$crate::__quote!(@PUNCT ':', ':')};
104 ( . ) => {$crate::__quote!(@PUNCT '.')};
105 ( < ) => {$crate::__quote!(@PUNCT '<')};
106 ( > ) => {$crate::__quote!(@PUNCT '>')};
107
108 ( $first:tt $($tail:tt)+ ) => {
109 {
110 let mut tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($first));
111 let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
112
113 tokens.append(&mut tail_tokens);
114 tokens
115 }
116 };
117}
118
119/// FIXME:
120/// It probably should implement in proc-macro
121#[macro_export]
122macro_rules! quote {
123 ( $($tt:tt)* ) => {
124 $crate::quote::IntoTt::to_subtree($crate::__quote!($($tt)*))
125 }
126}
127
128pub(crate) trait IntoTt {
129 fn to_subtree(self) -> tt::Subtree;
130 fn to_tokens(self) -> Vec<tt::TokenTree>;
131}
132
133impl IntoTt for Vec<tt::TokenTree> {
134 fn to_subtree(self) -> tt::Subtree {
135 tt::Subtree { delimiter: None, token_trees: self }
136 }
137
138 fn to_tokens(self) -> Vec<tt::TokenTree> {
139 self
140 }
141}
142
143impl IntoTt for tt::Subtree {
144 fn to_subtree(self) -> tt::Subtree {
145 self
146 }
147
148 fn to_tokens(self) -> Vec<tt::TokenTree> {
149 vec![tt::TokenTree::Subtree(self)]
150 }
151}
152
153pub(crate) trait ToTokenTree {
154 fn to_token(self) -> tt::TokenTree;
155}
156
157impl ToTokenTree for tt::TokenTree {
158 fn to_token(self) -> tt::TokenTree {
159 self
160 }
161}
162
163impl ToTokenTree for tt::Subtree {
164 fn to_token(self) -> tt::TokenTree {
165 self.into()
166 }
167}
168
169macro_rules! impl_to_to_tokentrees {
170 ($($ty:ty => $this:ident $im:block);*) => {
171 $(
172 impl ToTokenTree for $ty {
173 fn to_token($this) -> tt::TokenTree {
174 let leaf: tt::Leaf = $im.into();
175 leaf.into()
176 }
177 }
178
179 impl ToTokenTree for &$ty {
180 fn to_token($this) -> tt::TokenTree {
181 let leaf: tt::Leaf = $im.clone().into();
182 leaf.into()
183 }
184 }
185 )*
186 }
187}
188
189impl_to_to_tokentrees! {
190 u32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
191 usize => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()}};
192 i32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()}};
193 tt::Leaf => self { self };
194 tt::Literal => self { self };
195 tt::Ident => self { self };
196 tt::Punct => self { self };
197 &str => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into(), id: tt::TokenId::unspecified()}};
198 String => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into(), id: tt::TokenId::unspecified()}}
199}
200
201#[cfg(test)]
202mod tests {
203 #[test]
204 fn test_quote_delimiters() {
205 assert_eq!(quote!({}).to_string(), "{}");
206 assert_eq!(quote!(()).to_string(), "()");
207 assert_eq!(quote!([]).to_string(), "[]");
208 }
209
210 #[test]
211 fn test_quote_idents() {
212 assert_eq!(quote!(32).to_string(), "32");
213 assert_eq!(quote!(struct).to_string(), "struct");
214 }
215
216 #[test]
217 fn test_quote_hash_simple_literal() {
218 let a = 20;
219 assert_eq!(quote!(#a).to_string(), "20");
220 let s: String = "hello".into();
221 assert_eq!(quote!(#s).to_string(), "\"hello\"");
222 }
223
224 fn mk_ident(name: &str) -> tt::Ident {
225 tt::Ident { text: name.into(), id: tt::TokenId::unspecified() }
226 }
227
228 #[test]
229 fn test_quote_hash_token_tree() {
230 let a = mk_ident("hello");
231
232 let quoted = quote!(#a);
233 assert_eq!(quoted.to_string(), "hello");
234 let t = format!("{:?}", quoted);
235 assert_eq!(t, "SUBTREE $\n IDENT hello 4294967295");
236 }
237
238 #[test]
239 fn test_quote_simple_derive_copy() {
240 let name = mk_ident("Foo");
241
242 let quoted = quote! {
243 impl Clone for #name {
244 fn clone(&self) -> Self {
245 Self {}
246 }
247 }
248 };
249
250 assert_eq!(quoted.to_string(), "impl Clone for Foo {fn clone (& self) -> Self {Self {}}}");
251 }
252
253 #[test]
254 fn test_quote_derive_copy_hack() {
255 // Assume the given struct is:
256 // struct Foo {
257 // name: String,
258 // id: u32,
259 // }
260 let struct_name = mk_ident("Foo");
261 let fields = [mk_ident("name"), mk_ident("id")];
262 let fields = fields.iter().map(|it| quote!(#it: self.#it.clone(), ).token_trees).flatten();
263
264 let list = tt::Subtree {
265 delimiter: Some(tt::Delimiter {
266 kind: tt::DelimiterKind::Brace,
267 id: tt::TokenId::unspecified(),
268 }),
269 token_trees: fields.collect(),
270 };
271
272 let quoted = quote! {
273 impl Clone for #struct_name {
274 fn clone(&self) -> Self {
275 Self #list
276 }
277 }
278 };
279
280 assert_eq!(quoted.to_string(), "impl Clone for Foo {fn clone (& self) -> Self {Self {name : self . name . clone () , id : self . id . clone () ,}}}");
281 }
282}
diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs
deleted file mode 100644
index 332fa556f..000000000
--- a/crates/ra_hir_expand/src/test_db.rs
+++ /dev/null
@@ -1,49 +0,0 @@
1//! Database used for testing `hir_expand`.
2
3use std::{
4 fmt, panic,
5 sync::{Arc, Mutex},
6};
7
8use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate};
9use rustc_hash::FxHashSet;
10
11#[salsa::database(
12 ra_db::SourceDatabaseExtStorage,
13 ra_db::SourceDatabaseStorage,
14 crate::db::AstDatabaseStorage
15)]
16#[derive(Default)]
17pub struct TestDB {
18 storage: salsa::Storage<TestDB>,
19 events: Mutex<Option<Vec<salsa::Event>>>,
20}
21
22impl fmt::Debug for TestDB {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 f.debug_struct("TestDB").finish()
25 }
26}
27
28impl salsa::Database for TestDB {
29 fn salsa_event(&self, event: salsa::Event) {
30 let mut events = self.events.lock().unwrap();
31 if let Some(events) = &mut *events {
32 events.push(event);
33 }
34 }
35}
36
37impl panic::RefUnwindSafe for TestDB {}
38
39impl FileLoader for TestDB {
40 fn file_text(&self, file_id: FileId) -> Arc<String> {
41 FileLoaderDelegate(self).file_text(file_id)
42 }
43 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
44 FileLoaderDelegate(self).resolve_path(anchor, path)
45 }
46 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
47 FileLoaderDelegate(self).relevant_crates(file_id)
48 }
49}