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/input.rs | |
parent | c0dd36fd425416f5465abf7f12f5d3a14b35751d (diff) |
Rewrite `#[derive]` removal to be based on AST
Diffstat (limited to 'crates/hir_expand/src/input.rs')
-rw-r--r-- | crates/hir_expand/src/input.rs | 87 |
1 files changed, 87 insertions, 0 deletions
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 | } | ||