aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/macros.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/macros.rs')
-rw-r--r--crates/ra_hir/src/macros.rs146
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 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_db::{SyntaxDatabase, LocalSyntaxPtr}; 3use ra_db::{LocalSyntaxPtr, LocationIntener};
4use ra_syntax::{ 4use ra_syntax::{
5 TextRange, TextUnit, SourceFileNode, AstNode, SyntaxNode, 5 TextRange, TextUnit, SourceFileNode, AstNode, SyntaxNode,
6 ast, 6 ast,
7}; 7};
8 8
9use 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)]
14pub struct MacroInvocationId(u32);
15ra_db::impl_numeric_id!(MacroInvocationId);
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub struct MacroInvocationLoc {
19 source_root_id: SourceRootId,
20 module_id: ModuleId,
21 source_item_id: SourceItemId,
22}
23
24impl 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
33impl 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)]
11pub enum MacroDef { 45pub enum MacroDef {
12 CTry, 46 CTry,
13} 47}
14 48
49impl 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)]
16pub struct MacroInput { 107pub 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
28salsa::query_group! {
29
30pub trait MacroDatabase: SyntaxDatabase {
31 fn expand_macro(def: MacroDef, input: MacroInput) -> Option<Arc<MacroExpansion>> {
32 type ExpandMacroQuery;
33 }
34}
35
36}
37
38fn 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
68impl MacroExpansion { 119impl 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
151pub(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}