hir_expand/builtin/
attr_macro.rs1use intern::sym;
3use span::Span;
4
5use crate::{ExpandResult, MacroCallId, MacroCallKind, db::ExpandDatabase, name, tt};
6
7use super::quote;
8
9macro_rules! register_builtin {
10 ($(($name:ident, $variant:ident) => $expand:ident),* ) => {
11 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12 pub enum BuiltinAttrExpander {
13 $($variant),*
14 }
15
16 impl BuiltinAttrExpander {
17 pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult<tt::TopSubtree> {
18 match *self {
19 $( BuiltinAttrExpander::$variant => $expand, )*
20 }
21 }
22
23 fn find_by_name(name: &name::Name) -> Option<Self> {
24 match name {
25 $( id if id == &sym::$name => Some(BuiltinAttrExpander::$variant), )*
26 _ => None,
27 }
28 }
29 }
30
31 };
32}
33
34impl BuiltinAttrExpander {
35 pub fn expand(
36 &self,
37 db: &dyn ExpandDatabase,
38 id: MacroCallId,
39 tt: &tt::TopSubtree,
40 span: Span,
41 ) -> ExpandResult<tt::TopSubtree> {
42 self.expander()(db, id, tt, span)
43 }
44
45 pub fn is_derive(self) -> bool {
46 matches!(self, BuiltinAttrExpander::Derive | BuiltinAttrExpander::DeriveConst)
47 }
48 pub fn is_test(self) -> bool {
49 matches!(self, BuiltinAttrExpander::Test)
50 }
51 pub fn is_bench(self) -> bool {
52 matches!(self, BuiltinAttrExpander::Bench)
53 }
54 pub fn is_test_case(self) -> bool {
55 matches!(self, BuiltinAttrExpander::TestCase)
56 }
57}
58
59register_builtin! {
60 (bench, Bench) => dummy_gate_test_expand,
61 (cfg_accessible, CfgAccessible) => dummy_attr_expand,
62 (cfg_eval, CfgEval) => dummy_attr_expand,
63 (derive, Derive) => derive_expand,
64 (derive_const, DeriveConst) => derive_expand,
66 (global_allocator, GlobalAllocator) => dummy_attr_expand,
67 (test, Test) => dummy_gate_test_expand,
68 (test_case, TestCase) => dummy_gate_test_expand
69}
70
71pub fn find_builtin_attr(ident: &name::Name) -> Option<BuiltinAttrExpander> {
72 BuiltinAttrExpander::find_by_name(ident)
73}
74
75fn dummy_attr_expand(
76 _db: &dyn ExpandDatabase,
77 _id: MacroCallId,
78 tt: &tt::TopSubtree,
79 _span: Span,
80) -> ExpandResult<tt::TopSubtree> {
81 ExpandResult::ok(tt.clone())
82}
83
84fn dummy_gate_test_expand(
85 _db: &dyn ExpandDatabase,
86 _id: MacroCallId,
87 tt: &tt::TopSubtree,
88 span: Span,
89) -> ExpandResult<tt::TopSubtree> {
90 let result = quote::quote! { span=>
91 #[cfg(test)]
92 #tt
93 };
94 ExpandResult::ok(result)
95}
96
97fn derive_expand(
119 db: &dyn ExpandDatabase,
120 id: MacroCallId,
121 tt: &tt::TopSubtree,
122 span: Span,
123) -> ExpandResult<tt::TopSubtree> {
124 let loc = db.lookup_intern_macro_call(id);
125 let derives = match &loc.kind {
126 MacroCallKind::Attr { attr_args: Some(attr_args), .. } if loc.def.is_attribute_derive() => {
127 attr_args
128 }
129 _ => {
130 return ExpandResult::ok(tt::TopSubtree::empty(tt::DelimSpan {
131 open: span,
132 close: span,
133 }));
134 }
135 };
136 pseudo_derive_attr_expansion(tt, derives, span)
137}
138
139pub fn pseudo_derive_attr_expansion(
140 _: &tt::TopSubtree,
141 args: &tt::TopSubtree,
142 call_site: Span,
143) -> ExpandResult<tt::TopSubtree> {
144 let mk_leaf =
145 |char| tt::Leaf::Punct(tt::Punct { char, spacing: tt::Spacing::Alone, span: call_site });
146
147 let mut token_trees = tt::TopSubtreeBuilder::new(args.top_subtree().delimiter);
148 let iter = args.token_trees().split(|tt| {
149 matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. })))
150 });
151 for tts in iter {
152 token_trees.extend([mk_leaf('#'), mk_leaf('!')]);
153 token_trees.open(tt::DelimiterKind::Bracket, call_site);
154 token_trees.extend_with_tt(tts);
155 token_trees.close(call_site);
156 }
157 ExpandResult::ok(token_trees.build())
158}