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