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 (define_opaque, DefineOpaque) => dummy_attr_expand,
70}
71
72pub fn find_builtin_attr(ident: &name::Name) -> Option<BuiltinAttrExpander> {
73 BuiltinAttrExpander::find_by_name(ident)
74}
75
76fn dummy_attr_expand(
77 _db: &dyn ExpandDatabase,
78 _id: MacroCallId,
79 tt: &tt::TopSubtree,
80 _span: Span,
81) -> ExpandResult<tt::TopSubtree> {
82 ExpandResult::ok(tt.clone())
83}
84
85fn dummy_gate_test_expand(
86 _db: &dyn ExpandDatabase,
87 _id: MacroCallId,
88 tt: &tt::TopSubtree,
89 span: Span,
90) -> ExpandResult<tt::TopSubtree> {
91 let result = quote::quote! { span=>
92 #[cfg(test)]
93 #tt
94 };
95 ExpandResult::ok(result)
96}
97
98fn derive_expand(
120 db: &dyn ExpandDatabase,
121 id: MacroCallId,
122 tt: &tt::TopSubtree,
123 span: Span,
124) -> ExpandResult<tt::TopSubtree> {
125 let loc = db.lookup_intern_macro_call(id);
126 let derives = match &loc.kind {
127 MacroCallKind::Attr { attr_args: Some(attr_args), .. } if loc.def.is_attribute_derive() => {
128 attr_args
129 }
130 _ => {
131 return ExpandResult::ok(tt::TopSubtree::empty(tt::DelimSpan {
132 open: span,
133 close: span,
134 }));
135 }
136 };
137 pseudo_derive_attr_expansion(tt, derives, span)
138}
139
140pub fn pseudo_derive_attr_expansion(
141 _: &tt::TopSubtree,
142 args: &tt::TopSubtree,
143 call_site: Span,
144) -> ExpandResult<tt::TopSubtree> {
145 let mk_leaf =
146 |char| tt::Leaf::Punct(tt::Punct { char, spacing: tt::Spacing::Alone, span: call_site });
147
148 let mut token_trees = tt::TopSubtreeBuilder::new(args.top_subtree().delimiter);
149 let iter = args.token_trees().split(|tt| {
150 matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. })))
151 });
152 for tts in iter {
153 token_trees.extend([mk_leaf('#'), mk_leaf('!')]);
154 token_trees.open(tt::DelimiterKind::Bracket, call_site);
155 token_trees.extend_with_tt(tts);
156 token_trees.close(call_site);
157 }
158 ExpandResult::ok(token_trees.build())
159}