aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_expand/src/proc_macro.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_expand/src/proc_macro.rs')
-rw-r--r--crates/hir_expand/src/proc_macro.rs143
1 files changed, 143 insertions, 0 deletions
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
new file mode 100644
index 000000000..80255ea32
--- /dev/null
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -0,0 +1,143 @@
1//! Proc Macro Expander stub
2
3use crate::{db::AstDatabase, LazyMacroId};
4use base_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}