diff options
Diffstat (limited to 'crates/ra_hir/src/macros.rs')
-rw-r--r-- | crates/ra_hir/src/macros.rs | 146 |
1 files changed, 105 insertions, 41 deletions
diff --git a/crates/ra_hir/src/macros.rs b/crates/ra_hir/src/macros.rs index ef6502524..f0b99cc1a 100644 --- a/crates/ra_hir/src/macros.rs +++ b/crates/ra_hir/src/macros.rs | |||
@@ -1,17 +1,108 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_db::{SyntaxDatabase, LocalSyntaxPtr}; | 3 | use ra_db::{LocalSyntaxPtr, LocationIntener}; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | TextRange, TextUnit, SourceFileNode, AstNode, SyntaxNode, | 5 | TextRange, TextUnit, SourceFileNode, AstNode, SyntaxNode, |
6 | ast, | 6 | ast, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | use crate::{SourceRootId, module::ModuleId, SourceItemId, HirDatabase}; | ||
10 | |||
11 | /// Def's are a core concept of hir. A `Def` is an Item (function, module, etc) | ||
12 | /// in a specific module. | ||
13 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
14 | pub struct MacroInvocationId(u32); | ||
15 | ra_db::impl_numeric_id!(MacroInvocationId); | ||
16 | |||
17 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
18 | pub struct MacroInvocationLoc { | ||
19 | source_root_id: SourceRootId, | ||
20 | module_id: ModuleId, | ||
21 | source_item_id: SourceItemId, | ||
22 | } | ||
23 | |||
24 | impl MacroInvocationId { | ||
25 | pub(crate) fn loc( | ||
26 | self, | ||
27 | db: &impl AsRef<LocationIntener<MacroInvocationLoc, MacroInvocationId>>, | ||
28 | ) -> MacroInvocationLoc { | ||
29 | db.as_ref().id2loc(self) | ||
30 | } | ||
31 | } | ||
32 | |||
33 | impl MacroInvocationLoc { | ||
34 | #[allow(unused)] | ||
35 | pub(crate) fn id( | ||
36 | &self, | ||
37 | db: &impl AsRef<LocationIntener<MacroInvocationLoc, MacroInvocationId>>, | ||
38 | ) -> MacroInvocationId { | ||
39 | db.as_ref().loc2id(&self) | ||
40 | } | ||
41 | } | ||
42 | |||
9 | // Hard-coded defs for now :-( | 43 | // Hard-coded defs for now :-( |
10 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 44 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
11 | pub enum MacroDef { | 45 | pub enum MacroDef { |
12 | CTry, | 46 | CTry, |
13 | } | 47 | } |
14 | 48 | ||
49 | impl MacroDef { | ||
50 | pub fn ast_expand(macro_call: ast::MacroCall) -> Option<(TextUnit, MacroExpansion)> { | ||
51 | let (def, input) = MacroDef::from_call(macro_call)?; | ||
52 | let exp = def.expand(input)?; | ||
53 | let off = macro_call.token_tree()?.syntax().range().start(); | ||
54 | Some((off, exp)) | ||
55 | } | ||
56 | |||
57 | fn from_call(macro_call: ast::MacroCall) -> Option<(MacroDef, MacroInput)> { | ||
58 | let def = { | ||
59 | let path = macro_call.path()?; | ||
60 | if path.qualifier().is_some() { | ||
61 | return None; | ||
62 | } | ||
63 | let name_ref = path.segment()?.name_ref()?; | ||
64 | if name_ref.text() != "ctry" { | ||
65 | return None; | ||
66 | } | ||
67 | MacroDef::CTry | ||
68 | }; | ||
69 | |||
70 | let input = { | ||
71 | let arg = macro_call.token_tree()?.syntax(); | ||
72 | MacroInput { | ||
73 | text: arg.text().to_string(), | ||
74 | } | ||
75 | }; | ||
76 | Some((def, input)) | ||
77 | } | ||
78 | |||
79 | fn expand(self, input: MacroInput) -> Option<MacroExpansion> { | ||
80 | let MacroDef::CTry = self; | ||
81 | let text = format!( | ||
82 | r" | ||
83 | fn dummy() {{ | ||
84 | match {} {{ | ||
85 | None => return Ok(None), | ||
86 | Some(it) => it, | ||
87 | }} | ||
88 | }}", | ||
89 | input.text | ||
90 | ); | ||
91 | let file = SourceFileNode::parse(&text); | ||
92 | let match_expr = file.syntax().descendants().find_map(ast::MatchExpr::cast)?; | ||
93 | let match_arg = match_expr.expr()?; | ||
94 | let ptr = LocalSyntaxPtr::new(match_arg.syntax()); | ||
95 | let src_range = TextRange::offset_len(0.into(), TextUnit::of_str(&input.text)); | ||
96 | let ranges_map = vec![(src_range, match_arg.syntax().range())]; | ||
97 | let res = MacroExpansion { | ||
98 | text, | ||
99 | ranges_map, | ||
100 | ptr, | ||
101 | }; | ||
102 | Some(res) | ||
103 | } | ||
104 | } | ||
105 | |||
15 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 106 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
16 | pub struct MacroInput { | 107 | pub struct MacroInput { |
17 | // Should be token trees | 108 | // Should be token trees |
@@ -25,46 +116,6 @@ pub struct MacroExpansion { | |||
25 | ptr: LocalSyntaxPtr, | 116 | ptr: LocalSyntaxPtr, |
26 | } | 117 | } |
27 | 118 | ||
28 | salsa::query_group! { | ||
29 | |||
30 | pub trait MacroDatabase: SyntaxDatabase { | ||
31 | fn expand_macro(def: MacroDef, input: MacroInput) -> Option<Arc<MacroExpansion>> { | ||
32 | type ExpandMacroQuery; | ||
33 | } | ||
34 | } | ||
35 | |||
36 | } | ||
37 | |||
38 | fn expand_macro( | ||
39 | _db: &impl MacroDatabase, | ||
40 | def: MacroDef, | ||
41 | input: MacroInput, | ||
42 | ) -> Option<Arc<MacroExpansion>> { | ||
43 | let MacroDef::CTry = def; | ||
44 | let text = format!( | ||
45 | r" | ||
46 | fn dummy() {{ | ||
47 | match {} {{ | ||
48 | None => return Ok(None), | ||
49 | Some(it) => it, | ||
50 | }} | ||
51 | }}", | ||
52 | input.text | ||
53 | ); | ||
54 | let file = SourceFileNode::parse(&text); | ||
55 | let match_expr = file.syntax().descendants().find_map(ast::MatchExpr::cast)?; | ||
56 | let match_arg = match_expr.expr()?; | ||
57 | let ptr = LocalSyntaxPtr::new(match_arg.syntax()); | ||
58 | let src_range = TextRange::offset_len(0.into(), TextUnit::of_str(&input.text)); | ||
59 | let ranges_map = vec![(src_range, match_arg.syntax().range())]; | ||
60 | let res = MacroExpansion { | ||
61 | text, | ||
62 | ranges_map, | ||
63 | ptr, | ||
64 | }; | ||
65 | Some(Arc::new(res)) | ||
66 | } | ||
67 | |||
68 | impl MacroExpansion { | 119 | impl MacroExpansion { |
69 | pub fn file(&self) -> SourceFileNode { | 120 | pub fn file(&self) -> SourceFileNode { |
70 | SourceFileNode::parse(&self.text) | 121 | SourceFileNode::parse(&self.text) |
@@ -96,3 +147,16 @@ impl MacroExpansion { | |||
96 | None | 147 | None |
97 | } | 148 | } |
98 | } | 149 | } |
150 | |||
151 | pub(crate) fn expand_macro_invocation( | ||
152 | db: &impl HirDatabase, | ||
153 | invoc: MacroInvocationId, | ||
154 | ) -> Option<Arc<MacroExpansion>> { | ||
155 | let loc = invoc.loc(db); | ||
156 | let syntax = db.file_item(loc.source_item_id); | ||
157 | let syntax = syntax.borrowed(); | ||
158 | let macro_call = ast::MacroCall::cast(syntax).unwrap(); | ||
159 | |||
160 | let (def, input) = MacroDef::from_call(macro_call)?; | ||
161 | def.expand(input).map(Arc::new) | ||
162 | } | ||