diff options
author | Igor Aleksanov <[email protected]> | 2020-08-14 05:34:07 +0100 |
---|---|---|
committer | Igor Aleksanov <[email protected]> | 2020-08-14 05:34:07 +0100 |
commit | c26c911ec1e6c2ad1dcb7d155a6a1d528839ad1a (patch) | |
tree | 7cff36c38234be0afb65273146d8247083a5cfeb /crates/hir_expand/src/proc_macro.rs | |
parent | 3c018bf84de5c693b5ee1c6bec0fed3b201c2060 (diff) | |
parent | f1f73649a686dc6e6449afc35e0fa6fed00e225d (diff) |
Merge branch 'master' into add-disable-diagnostics
Diffstat (limited to 'crates/hir_expand/src/proc_macro.rs')
-rw-r--r-- | crates/hir_expand/src/proc_macro.rs | 143 |
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 | |||
3 | use crate::{db::AstDatabase, LazyMacroId}; | ||
4 | use base_db::{CrateId, ProcMacroId}; | ||
5 | use tt::buffer::{Cursor, TokenBuffer}; | ||
6 | |||
7 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] | ||
8 | pub struct ProcMacroExpander { | ||
9 | krate: CrateId, | ||
10 | proc_macro_id: ProcMacroId, | ||
11 | } | ||
12 | |||
13 | macro_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 | |||
22 | impl 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 | |||
47 | fn 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 | |||
57 | fn 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 | |||
67 | fn 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 | |||
77 | fn 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)] | ||
104 | mod 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#" | ||
127 | SUBTREE $ | ||
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 | } | ||