diff options
-rw-r--r-- | crates/ra_hir_expand/src/builtin_macro.rs | 163 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/db.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/lib.rs | 7 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/test_db.rs | 50 |
4 files changed, 184 insertions, 42 deletions
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index 4da56529d..c0e0436c0 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs | |||
@@ -8,47 +8,47 @@ use crate::{ | |||
8 | 8 | ||
9 | use crate::quote; | 9 | use crate::quote; |
10 | 10 | ||
11 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 11 | macro_rules! register_builtin { |
12 | pub enum BuiltinExpander { | 12 | ( $(($name:ident, $kind: ident) => $expand:ident),* ) => { |
13 | Column, | 13 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
14 | File, | 14 | pub enum BuiltinFnLikeExpander { |
15 | Line, | 15 | $($kind),* |
16 | Stringify, | 16 | } |
17 | } | ||
18 | 17 | ||
19 | impl BuiltinExpander { | 18 | impl BuiltinFnLikeExpander { |
20 | pub fn expand( | 19 | pub fn expand( |
21 | &self, | 20 | &self, |
22 | db: &dyn AstDatabase, | 21 | db: &dyn AstDatabase, |
23 | id: MacroCallId, | 22 | id: MacroCallId, |
24 | tt: &tt::Subtree, | 23 | tt: &tt::Subtree, |
25 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 24 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
26 | match self { | 25 | let expander = match *self { |
27 | BuiltinExpander::Column => column_expand(db, id, tt), | 26 | $( BuiltinFnLikeExpander::$kind => $expand, )* |
28 | BuiltinExpander::File => file_expand(db, id, tt), | 27 | }; |
29 | BuiltinExpander::Line => line_expand(db, id, tt), | 28 | expander(db, id, tt) |
30 | BuiltinExpander::Stringify => stringify_expand(db, id, tt), | 29 | } |
31 | } | 30 | } |
32 | } | 31 | |
32 | pub fn find_builtin_macro( | ||
33 | ident: &name::Name, | ||
34 | krate: CrateId, | ||
35 | ast_id: AstId<ast::MacroCall>, | ||
36 | ) -> Option<MacroDefId> { | ||
37 | let kind = match ident { | ||
38 | $( id if id == &name::$name => BuiltinFnLikeExpander::$kind, )* | ||
39 | _ => return None, | ||
40 | }; | ||
41 | |||
42 | Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(kind) }) | ||
43 | } | ||
44 | }; | ||
33 | } | 45 | } |
34 | 46 | ||
35 | pub fn find_builtin_macro( | 47 | register_builtin! { |
36 | ident: &name::Name, | 48 | (COLUMN_MACRO, Column) => column_expand, |
37 | krate: CrateId, | 49 | (FILE_MACRO, File) => file_expand, |
38 | ast_id: AstId<ast::MacroCall>, | 50 | (LINE_MACRO, Line) => line_expand, |
39 | ) -> Option<MacroDefId> { | 51 | (STRINGIFY_MACRO, Stringify) => stringify_expand |
40 | // FIXME: Better registering method | ||
41 | if ident == &name::COLUMN_MACRO { | ||
42 | Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Column) }) | ||
43 | } else if ident == &name::FILE_MACRO { | ||
44 | Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::File) }) | ||
45 | } else if ident == &name::LINE_MACRO { | ||
46 | Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Line) }) | ||
47 | } else if ident == &name::STRINGIFY_MACRO { | ||
48 | Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Stringify) }) | ||
49 | } else { | ||
50 | None | ||
51 | } | ||
52 | } | 52 | } |
53 | 53 | ||
54 | fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { | 54 | fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { |
@@ -171,3 +171,92 @@ fn file_expand( | |||
171 | 171 | ||
172 | Ok(expanded) | 172 | Ok(expanded) |
173 | } | 173 | } |
174 | |||
175 | #[cfg(test)] | ||
176 | mod tests { | ||
177 | use super::*; | ||
178 | use crate::{test_db::TestDB, MacroCallLoc}; | ||
179 | use ra_db::{fixture::WithFixture, SourceDatabase}; | ||
180 | |||
181 | fn expand_builtin_macro(s: &str, expander: BuiltinFnLikeExpander) -> String { | ||
182 | let (db, file_id) = TestDB::with_single_file(&s); | ||
183 | let parsed = db.parse(file_id); | ||
184 | let macro_calls: Vec<_> = | ||
185 | parsed.syntax_node().descendants().filter_map(|it| ast::MacroCall::cast(it)).collect(); | ||
186 | |||
187 | let ast_id_map = db.ast_id_map(file_id.into()); | ||
188 | |||
189 | // the first one should be a macro_rules | ||
190 | let def = MacroDefId { | ||
191 | krate: CrateId(0), | ||
192 | ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[0])), | ||
193 | kind: MacroDefKind::BuiltIn(expander), | ||
194 | }; | ||
195 | |||
196 | let loc = MacroCallLoc { | ||
197 | def, | ||
198 | ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_calls[1])), | ||
199 | }; | ||
200 | |||
201 | let id = db.intern_macro(loc); | ||
202 | let parsed = db.parse_or_expand(id.as_file(MacroFileKind::Expr)).unwrap(); | ||
203 | |||
204 | parsed.text().to_string() | ||
205 | } | ||
206 | |||
207 | #[test] | ||
208 | fn test_column_expand() { | ||
209 | let expanded = expand_builtin_macro( | ||
210 | r#" | ||
211 | #[rustc_builtin_macro] | ||
212 | macro_rules! column {() => {}} | ||
213 | column!() | ||
214 | "#, | ||
215 | BuiltinFnLikeExpander::Column, | ||
216 | ); | ||
217 | |||
218 | assert_eq!(expanded, "9"); | ||
219 | } | ||
220 | |||
221 | #[test] | ||
222 | fn test_line_expand() { | ||
223 | let expanded = expand_builtin_macro( | ||
224 | r#" | ||
225 | #[rustc_builtin_macro] | ||
226 | macro_rules! line {() => {}} | ||
227 | line!() | ||
228 | "#, | ||
229 | BuiltinFnLikeExpander::Line, | ||
230 | ); | ||
231 | |||
232 | assert_eq!(expanded, "4"); | ||
233 | } | ||
234 | |||
235 | #[test] | ||
236 | fn test_stringify_expand() { | ||
237 | let expanded = expand_builtin_macro( | ||
238 | r#" | ||
239 | #[rustc_builtin_macro] | ||
240 | macro_rules! stringify {() => {}} | ||
241 | stringify!(a b c) | ||
242 | "#, | ||
243 | BuiltinFnLikeExpander::Stringify, | ||
244 | ); | ||
245 | |||
246 | assert_eq!(expanded, "\"a b c\""); | ||
247 | } | ||
248 | |||
249 | #[test] | ||
250 | fn test_file_expand() { | ||
251 | let expanded = expand_builtin_macro( | ||
252 | r#" | ||
253 | #[rustc_builtin_macro] | ||
254 | macro_rules! file {() => {}} | ||
255 | file!() | ||
256 | "#, | ||
257 | BuiltinFnLikeExpander::File, | ||
258 | ); | ||
259 | |||
260 | assert_eq!(expanded, "\"\""); | ||
261 | } | ||
262 | } | ||
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index e1d93a8ef..8e46fa177 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs | |||
@@ -9,14 +9,14 @@ use ra_prof::profile; | |||
9 | use ra_syntax::{AstNode, Parse, SyntaxNode}; | 9 | use ra_syntax::{AstNode, Parse, SyntaxNode}; |
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | ast_id_map::AstIdMap, BuiltinExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, | 12 | ast_id_map::AstIdMap, BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, |
13 | MacroDefId, MacroDefKind, MacroFile, MacroFileKind, | 13 | MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, MacroFileKind, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | #[derive(Debug, Clone, Eq, PartialEq)] | 16 | #[derive(Debug, Clone, Eq, PartialEq)] |
17 | pub enum TokenExpander { | 17 | pub enum TokenExpander { |
18 | MacroRules(mbe::MacroRules), | 18 | MacroRules(mbe::MacroRules), |
19 | Builtin(BuiltinExpander), | 19 | Builtin(BuiltinFnLikeExpander), |
20 | } | 20 | } |
21 | 21 | ||
22 | impl TokenExpander { | 22 | impl TokenExpander { |
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 126d12fbb..4f3ccf1d0 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs | |||
@@ -24,7 +24,10 @@ use ra_syntax::{ | |||
24 | }; | 24 | }; |
25 | 25 | ||
26 | use crate::ast_id_map::FileAstId; | 26 | use crate::ast_id_map::FileAstId; |
27 | use crate::builtin_macro::BuiltinExpander; | 27 | use crate::builtin_macro::BuiltinFnLikeExpander; |
28 | |||
29 | #[cfg(test)] | ||
30 | mod test_db; | ||
28 | 31 | ||
29 | /// Input to the analyzer is a set of files, where each file is identified by | 32 | /// Input to the analyzer is a set of files, where each file is identified by |
30 | /// `FileId` and contains source code. However, another source of source code in | 33 | /// `FileId` and contains source code. However, another source of source code in |
@@ -135,7 +138,7 @@ pub struct MacroDefId { | |||
135 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 138 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
136 | pub enum MacroDefKind { | 139 | pub enum MacroDefKind { |
137 | Declarative, | 140 | Declarative, |
138 | BuiltIn(BuiltinExpander), | 141 | BuiltIn(BuiltinFnLikeExpander), |
139 | } | 142 | } |
140 | 143 | ||
141 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 144 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs new file mode 100644 index 000000000..d23e75d9e --- /dev/null +++ b/crates/ra_hir_expand/src/test_db.rs | |||
@@ -0,0 +1,50 @@ | |||
1 | //! Database used for testing `hir_expand`. | ||
2 | |||
3 | use std::{ | ||
4 | panic, | ||
5 | sync::{Arc, Mutex}, | ||
6 | }; | ||
7 | |||
8 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath}; | ||
9 | |||
10 | #[salsa::database( | ||
11 | ra_db::SourceDatabaseExtStorage, | ||
12 | ra_db::SourceDatabaseStorage, | ||
13 | crate::db::AstDatabaseStorage | ||
14 | )] | ||
15 | #[derive(Debug, Default)] | ||
16 | pub struct TestDB { | ||
17 | runtime: salsa::Runtime<TestDB>, | ||
18 | events: Mutex<Option<Vec<salsa::Event<TestDB>>>>, | ||
19 | } | ||
20 | |||
21 | impl salsa::Database for TestDB { | ||
22 | fn salsa_runtime(&self) -> &salsa::Runtime<Self> { | ||
23 | &self.runtime | ||
24 | } | ||
25 | |||
26 | fn salsa_event(&self, event: impl Fn() -> salsa::Event<TestDB>) { | ||
27 | let mut events = self.events.lock().unwrap(); | ||
28 | if let Some(events) = &mut *events { | ||
29 | events.push(event()); | ||
30 | } | ||
31 | } | ||
32 | } | ||
33 | |||
34 | impl panic::RefUnwindSafe for TestDB {} | ||
35 | |||
36 | impl FileLoader for TestDB { | ||
37 | fn file_text(&self, file_id: FileId) -> Arc<String> { | ||
38 | FileLoaderDelegate(self).file_text(file_id) | ||
39 | } | ||
40 | fn resolve_relative_path( | ||
41 | &self, | ||
42 | anchor: FileId, | ||
43 | relative_path: &RelativePath, | ||
44 | ) -> Option<FileId> { | ||
45 | FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) | ||
46 | } | ||
47 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | ||
48 | FileLoaderDelegate(self).relevant_crates(file_id) | ||
49 | } | ||
50 | } | ||