diff options
Diffstat (limited to 'crates/hir_expand/src')
-rw-r--r-- | crates/hir_expand/src/builtin_derive.rs | 8 | ||||
-rw-r--r-- | crates/hir_expand/src/db.rs | 7 | ||||
-rw-r--r-- | crates/hir_expand/src/input.rs | 95 | ||||
-rw-r--r-- | crates/hir_expand/src/lib.rs | 6 | ||||
-rw-r--r-- | crates/hir_expand/src/proc_macro.rs | 102 |
5 files changed, 110 insertions, 108 deletions
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs index 392079ed4..537c03028 100644 --- a/crates/hir_expand/src/builtin_derive.rs +++ b/crates/hir_expand/src/builtin_derive.rs | |||
@@ -269,7 +269,7 @@ mod tests { | |||
269 | use expect_test::{expect, Expect}; | 269 | use expect_test::{expect, Expect}; |
270 | use name::AsName; | 270 | use name::AsName; |
271 | 271 | ||
272 | use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc}; | 272 | use crate::{test_db::TestDB, AstId, AttrId, MacroCallId, MacroCallKind, MacroCallLoc}; |
273 | 273 | ||
274 | use super::*; | 274 | use super::*; |
275 | 275 | ||
@@ -317,7 +317,11 @@ $0 | |||
317 | local_inner: false, | 317 | local_inner: false, |
318 | }, | 318 | }, |
319 | krate: CrateId(0), | 319 | krate: CrateId(0), |
320 | kind: MacroCallKind::Derive { ast_id, derive_name: name.to_string() }, | 320 | kind: MacroCallKind::Derive { |
321 | ast_id, | ||
322 | derive_name: name.to_string(), | ||
323 | derive_attr: AttrId(0), | ||
324 | }, | ||
321 | }; | 325 | }; |
322 | 326 | ||
323 | let id: MacroCallId = db.intern_macro(loc).into(); | 327 | let id: MacroCallId = db.intern_macro(loc).into(); |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 10fe60821..95dc12744 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -14,9 +14,9 @@ use syntax::{ | |||
14 | }; | 14 | }; |
15 | 15 | ||
16 | use crate::{ | 16 | use crate::{ |
17 | ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinDeriveExpander, BuiltinFnLikeExpander, | 17 | ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander, |
18 | EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, | 18 | BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId, |
19 | MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, | 19 | MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, |
20 | }; | 20 | }; |
21 | 21 | ||
22 | /// Total limit on the number of tokens produced by any macro invocation. | 22 | /// Total limit on the number of tokens produced by any macro invocation. |
@@ -191,6 +191,7 @@ fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { | |||
191 | }; | 191 | }; |
192 | let loc = db.lookup_intern_macro(id); | 192 | let loc = db.lookup_intern_macro(id); |
193 | let arg = loc.kind.arg(db)?; | 193 | let arg = loc.kind.arg(db)?; |
194 | let arg = process_macro_input(db, arg, id); | ||
194 | Some(arg.green()) | 195 | Some(arg.green()) |
195 | } | 196 | } |
196 | 197 | ||
diff --git a/crates/hir_expand/src/input.rs b/crates/hir_expand/src/input.rs new file mode 100644 index 000000000..d1f22aba4 --- /dev/null +++ b/crates/hir_expand/src/input.rs | |||
@@ -0,0 +1,95 @@ | |||
1 | //! Macro input conditioning. | ||
2 | |||
3 | use syntax::{ | ||
4 | ast::{self, AttrsOwner}, | ||
5 | AstNode, SyntaxNode, | ||
6 | }; | ||
7 | |||
8 | use crate::{ | ||
9 | db::AstDatabase, | ||
10 | name::{name, AsName}, | ||
11 | AttrId, LazyMacroId, MacroCallKind, MacroCallLoc, | ||
12 | }; | ||
13 | |||
14 | pub(crate) fn process_macro_input( | ||
15 | db: &dyn AstDatabase, | ||
16 | node: SyntaxNode, | ||
17 | id: LazyMacroId, | ||
18 | ) -> SyntaxNode { | ||
19 | let loc: MacroCallLoc = db.lookup_intern_macro(id); | ||
20 | |||
21 | match loc.kind { | ||
22 | MacroCallKind::FnLike { .. } => node, | ||
23 | MacroCallKind::Derive { derive_attr, .. } => { | ||
24 | let item = match ast::Item::cast(node.clone()) { | ||
25 | Some(item) => item, | ||
26 | None => return node, | ||
27 | }; | ||
28 | |||
29 | remove_derives_up_to(item, derive_attr).syntax().clone() | ||
30 | } | ||
31 | } | ||
32 | } | ||
33 | |||
34 | /// Removes `#[derive]` attributes from `item`, up to `attr`. | ||
35 | fn remove_derives_up_to(item: ast::Item, attr: AttrId) -> ast::Item { | ||
36 | let item = item.clone_for_update(); | ||
37 | let idx = attr.0 as usize; | ||
38 | for attr in item.attrs().take(idx + 1) { | ||
39 | if let Some(name) = | ||
40 | attr.path().and_then(|path| path.as_single_segment()).and_then(|seg| seg.name_ref()) | ||
41 | { | ||
42 | if name.as_name() == name![derive] { | ||
43 | attr.syntax().detach(); | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | item | ||
48 | } | ||
49 | |||
50 | #[cfg(test)] | ||
51 | mod tests { | ||
52 | use base_db::fixture::WithFixture; | ||
53 | use base_db::SourceDatabase; | ||
54 | use expect_test::{expect, Expect}; | ||
55 | |||
56 | use crate::test_db::TestDB; | ||
57 | |||
58 | use super::*; | ||
59 | |||
60 | fn test_remove_derives_up_to(attr: AttrId, ra_fixture: &str, expect: Expect) { | ||
61 | let (db, file_id) = TestDB::with_single_file(&ra_fixture); | ||
62 | let parsed = db.parse(file_id); | ||
63 | |||
64 | let mut items: Vec<_> = | ||
65 | parsed.syntax_node().descendants().filter_map(ast::Item::cast).collect(); | ||
66 | assert_eq!(items.len(), 1); | ||
67 | |||
68 | let item = remove_derives_up_to(items.pop().unwrap(), attr); | ||
69 | expect.assert_eq(&item.to_string()); | ||
70 | } | ||
71 | |||
72 | #[test] | ||
73 | fn remove_derive() { | ||
74 | test_remove_derives_up_to( | ||
75 | AttrId(2), | ||
76 | r#" | ||
77 | #[allow(unused)] | ||
78 | #[derive(Copy)] | ||
79 | #[derive(Hello)] | ||
80 | #[derive(Clone)] | ||
81 | struct A { | ||
82 | bar: u32 | ||
83 | } | ||
84 | "#, | ||
85 | expect![[r#" | ||
86 | #[allow(unused)] | ||
87 | |||
88 | |||
89 | #[derive(Clone)] | ||
90 | struct A { | ||
91 | bar: u32 | ||
92 | }"#]], | ||
93 | ); | ||
94 | } | ||
95 | } | ||
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index a179102f0..7349fdfe4 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -14,6 +14,7 @@ pub mod builtin_macro; | |||
14 | pub mod proc_macro; | 14 | pub mod proc_macro; |
15 | pub mod quote; | 15 | pub mod quote; |
16 | pub mod eager; | 16 | pub mod eager; |
17 | mod input; | ||
17 | 18 | ||
18 | use either::Either; | 19 | use either::Either; |
19 | pub use mbe::{ExpandError, ExpandResult}; | 20 | pub use mbe::{ExpandError, ExpandResult}; |
@@ -291,9 +292,12 @@ pub struct MacroCallLoc { | |||
291 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 292 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
292 | pub enum MacroCallKind { | 293 | pub enum MacroCallKind { |
293 | FnLike { ast_id: AstId<ast::MacroCall> }, | 294 | FnLike { ast_id: AstId<ast::MacroCall> }, |
294 | Derive { ast_id: AstId<ast::Item>, derive_name: String }, | 295 | Derive { ast_id: AstId<ast::Item>, derive_name: String, derive_attr: AttrId }, |
295 | } | 296 | } |
296 | 297 | ||
298 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
299 | pub struct AttrId(pub u32); | ||
300 | |||
297 | impl MacroCallKind { | 301 | impl MacroCallKind { |
298 | fn file_id(&self) -> HirFileId { | 302 | fn file_id(&self) -> HirFileId { |
299 | match self { | 303 | match self { |
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs index 75e950816..d5643393a 100644 --- a/crates/hir_expand/src/proc_macro.rs +++ b/crates/hir_expand/src/proc_macro.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | 2 | ||
3 | use crate::db::AstDatabase; | 3 | use crate::db::AstDatabase; |
4 | use base_db::{CrateId, ProcMacroId}; | 4 | use base_db::{CrateId, ProcMacroId}; |
5 | use tt::buffer::{Cursor, TokenBuffer}; | ||
6 | 5 | ||
7 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] | 6 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] |
8 | pub struct ProcMacroExpander { | 7 | pub struct ProcMacroExpander { |
@@ -44,9 +43,6 @@ impl ProcMacroExpander { | |||
44 | .clone() | 43 | .clone() |
45 | .ok_or_else(|| err!("No derive macro found."))?; | 44 | .ok_or_else(|| err!("No derive macro found."))?; |
46 | 45 | ||
47 | let tt = remove_derive_attrs(tt) | ||
48 | .ok_or_else(|| err!("Fail to remove derive for custom derive"))?; | ||
49 | |||
50 | // Proc macros have access to the environment variables of the invoking crate. | 46 | // Proc macros have access to the environment variables of the invoking crate. |
51 | let env = &krate_graph[calling_crate].env; | 47 | let env = &krate_graph[calling_crate].env; |
52 | 48 | ||
@@ -56,101 +52,3 @@ impl ProcMacroExpander { | |||
56 | } | 52 | } |
57 | } | 53 | } |
58 | } | 54 | } |
59 | |||
60 | fn eat_punct(cursor: &mut Cursor, c: char) -> bool { | ||
61 | if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = cursor.token_tree() { | ||
62 | if punct.char == c { | ||
63 | *cursor = cursor.bump(); | ||
64 | return true; | ||
65 | } | ||
66 | } | ||
67 | false | ||
68 | } | ||
69 | |||
70 | fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool { | ||
71 | if let Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) = cursor.token_tree() { | ||
72 | if Some(kind) == subtree.delimiter_kind() { | ||
73 | *cursor = cursor.bump_subtree(); | ||
74 | return true; | ||
75 | } | ||
76 | } | ||
77 | false | ||
78 | } | ||
79 | |||
80 | fn eat_ident(cursor: &mut Cursor, t: &str) -> bool { | ||
81 | if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(ident), _)) = cursor.token_tree() { | ||
82 | if t == ident.text.as_str() { | ||
83 | *cursor = cursor.bump(); | ||
84 | return true; | ||
85 | } | ||
86 | } | ||
87 | false | ||
88 | } | ||
89 | |||
90 | fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> { | ||
91 | let buffer = TokenBuffer::from_tokens(&tt.token_trees); | ||
92 | let mut p = buffer.begin(); | ||
93 | let mut result = tt::Subtree::default(); | ||
94 | |||
95 | while !p.eof() { | ||
96 | let curr = p; | ||
97 | |||
98 | if eat_punct(&mut p, '#') { | ||
99 | eat_punct(&mut p, '!'); | ||
100 | let parent = p; | ||
101 | if eat_subtree(&mut p, tt::DelimiterKind::Bracket) { | ||
102 | if eat_ident(&mut p, "derive") { | ||
103 | p = parent.bump(); | ||
104 | continue; | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | result.token_trees.push(curr.token_tree()?.cloned()); | ||
110 | p = curr.bump(); | ||
111 | } | ||
112 | |||
113 | Some(result) | ||
114 | } | ||
115 | |||
116 | #[cfg(test)] | ||
117 | mod tests { | ||
118 | use super::*; | ||
119 | use test_utils::assert_eq_text; | ||
120 | |||
121 | #[test] | ||
122 | fn test_remove_derive_attrs() { | ||
123 | let tt = mbe::parse_to_token_tree( | ||
124 | r#" | ||
125 | #[allow(unused)] | ||
126 | #[derive(Copy)] | ||
127 | #[derive(Hello)] | ||
128 | struct A { | ||
129 | bar: u32 | ||
130 | } | ||
131 | "#, | ||
132 | ) | ||
133 | .unwrap() | ||
134 | .0; | ||
135 | let result = format!("{:#?}", remove_derive_attrs(&tt).unwrap()); | ||
136 | |||
137 | assert_eq_text!( | ||
138 | r#" | ||
139 | SUBTREE $ | ||
140 | PUNCH # [alone] 0 | ||
141 | SUBTREE [] 1 | ||
142 | IDENT allow 2 | ||
143 | SUBTREE () 3 | ||
144 | IDENT unused 4 | ||
145 | IDENT struct 15 | ||
146 | IDENT A 16 | ||
147 | SUBTREE {} 17 | ||
148 | IDENT bar 18 | ||
149 | PUNCH : [alone] 19 | ||
150 | IDENT u32 20 | ||
151 | "# | ||
152 | .trim(), | ||
153 | &result | ||
154 | ); | ||
155 | } | ||
156 | } | ||