diff options
author | Jonas Schievink <[email protected]> | 2021-04-09 13:10:54 +0100 |
---|---|---|
committer | Jonas Schievink <[email protected]> | 2021-04-09 13:10:54 +0100 |
commit | 7e78aebc8fbbb4043d62949681e4d700f1a2ec46 (patch) | |
tree | 4050e82c30af415d13e1c9fd1dd7cda1fcce5211 /crates/hir_expand/src | |
parent | c0dd36fd425416f5465abf7f12f5d3a14b35751d (diff) |
Rewrite `#[derive]` removal to be based on AST
Diffstat (limited to 'crates/hir_expand/src')
-rw-r--r-- | crates/hir_expand/src/db.rs | 7 | ||||
-rw-r--r-- | crates/hir_expand/src/input.rs | 87 | ||||
-rw-r--r-- | crates/hir_expand/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/hir_expand/src/proc_macro.rs | 102 |
4 files changed, 92 insertions, 105 deletions
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..8f652cd7c --- /dev/null +++ b/crates/hir_expand/src/input.rs | |||
@@ -0,0 +1,87 @@ | |||
1 | //! Macro input conditioning. | ||
2 | |||
3 | use syntax::{ | ||
4 | ast::{self, AttrsOwner}, | ||
5 | AstNode, SyntaxNode, | ||
6 | }; | ||
7 | |||
8 | use crate::{db::AstDatabase, name::AsName, AttrId, LazyMacroId, MacroCallKind, MacroCallLoc}; | ||
9 | |||
10 | pub fn process_macro_input(db: &dyn AstDatabase, node: SyntaxNode, id: LazyMacroId) -> SyntaxNode { | ||
11 | let loc: MacroCallLoc = db.lookup_intern_macro(id); | ||
12 | |||
13 | match loc.kind { | ||
14 | MacroCallKind::FnLike { .. } => node, | ||
15 | MacroCallKind::Derive { derive_attr, .. } => { | ||
16 | let item = match ast::Item::cast(node.clone()) { | ||
17 | Some(item) => item, | ||
18 | None => return node, | ||
19 | }; | ||
20 | |||
21 | remove_derives_up_to(item, derive_attr).syntax().clone() | ||
22 | } | ||
23 | } | ||
24 | } | ||
25 | |||
26 | /// Removes `#[derive]` attributes from `item`, up to `attr`. | ||
27 | fn remove_derives_up_to(item: ast::Item, attr: AttrId) -> ast::Item { | ||
28 | let item = item.clone_for_update(); | ||
29 | let idx = attr.0 as usize; | ||
30 | for attr in item.attrs().take(idx + 1) { | ||
31 | if let Some(name) = | ||
32 | attr.path().and_then(|path| path.as_single_segment()).and_then(|seg| seg.name_ref()) | ||
33 | { | ||
34 | if name.as_name().to_string() == "derive" { | ||
35 | attr.syntax().detach(); | ||
36 | } | ||
37 | } | ||
38 | } | ||
39 | item | ||
40 | } | ||
41 | |||
42 | #[cfg(test)] | ||
43 | mod tests { | ||
44 | use base_db::fixture::WithFixture; | ||
45 | use base_db::SourceDatabase; | ||
46 | use expect_test::{expect, Expect}; | ||
47 | |||
48 | use crate::test_db::TestDB; | ||
49 | |||
50 | use super::*; | ||
51 | |||
52 | fn test_remove_derives_up_to(attr: AttrId, ra_fixture: &str, expect: Expect) { | ||
53 | let (db, file_id) = TestDB::with_single_file(&ra_fixture); | ||
54 | let parsed = db.parse(file_id); | ||
55 | |||
56 | let mut items: Vec<_> = | ||
57 | parsed.syntax_node().descendants().filter_map(ast::Item::cast).collect(); | ||
58 | assert_eq!(items.len(), 1); | ||
59 | |||
60 | let item = remove_derives_up_to(items.pop().unwrap(), attr); | ||
61 | expect.assert_eq(&item.to_string()); | ||
62 | } | ||
63 | |||
64 | #[test] | ||
65 | fn remove_derive() { | ||
66 | test_remove_derives_up_to( | ||
67 | AttrId(2), | ||
68 | r#" | ||
69 | #[allow(unused)] | ||
70 | #[derive(Copy)] | ||
71 | #[derive(Hello)] | ||
72 | #[derive(Clone)] | ||
73 | struct A { | ||
74 | bar: u32 | ||
75 | } | ||
76 | "#, | ||
77 | expect![[r#" | ||
78 | #[allow(unused)] | ||
79 | |||
80 | |||
81 | #[derive(Clone)] | ||
82 | struct A { | ||
83 | bar: u32 | ||
84 | }"#]], | ||
85 | ); | ||
86 | } | ||
87 | } | ||
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index a0e6aec62..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}; |
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 | } | ||