1 //! Builtin attributes.
3 use itertools::Itertools;
7 db::AstDatabase, name, AstId, CrateId, ExpandResult, MacroCallId, MacroCallKind, MacroDefId,
11 macro_rules! register_builtin {
12 ( $(($name:ident, $variant:ident) => $expand:ident),* ) => {
13 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14 pub enum BuiltinAttrExpander {
18 impl BuiltinAttrExpander {
24 ) -> ExpandResult<tt::Subtree> {
25 let expander = match *self {
26 $( BuiltinAttrExpander::$variant => $expand, )*
31 fn find_by_name(name: &name::Name) -> Option<Self> {
33 $( id if id == &name::name![$name] => Some(BuiltinAttrExpander::$variant), )*
42 impl BuiltinAttrExpander {
43 pub fn is_derive(self) -> bool {
44 matches!(self, BuiltinAttrExpander::Derive)
46 pub fn is_test(self) -> bool {
47 matches!(self, BuiltinAttrExpander::Test)
49 pub fn is_bench(self) -> bool {
50 matches!(self, BuiltinAttrExpander::Bench)
55 (bench, Bench) => dummy_attr_expand,
56 (cfg_accessible, CfgAccessible) => dummy_attr_expand,
57 (cfg_eval, CfgEval) => dummy_attr_expand,
58 (derive, Derive) => derive_attr_expand,
59 (global_allocator, GlobalAllocator) => dummy_attr_expand,
60 (test, Test) => dummy_attr_expand,
61 (test_case, TestCase) => dummy_attr_expand
64 pub fn find_builtin_attr(
67 ast_id: AstId<ast::Macro>,
68 ) -> Option<MacroDefId> {
69 let expander = BuiltinAttrExpander::find_by_name(ident)?;
72 kind: MacroDefKind::BuiltInAttr(expander, ast_id),
78 _db: &dyn AstDatabase,
81 ) -> ExpandResult<tt::Subtree> {
82 ExpandResult::ok(tt.clone())
85 fn derive_attr_expand(
89 ) -> ExpandResult<tt::Subtree> {
90 // we generate a very specific expansion here, as we do not actually expand the `#[derive]` attribute
91 // itself in name res, but we do want to expand it to something for the IDE layer, so that the input
92 // derive attributes can be downmapped, and resolved
93 // This is basically a hack, to get rid of hacks in the IDE layer that slowly accumulate more and more
96 // we transform the token tree of `#[derive(Foo, bar::Bar)]` into
102 // which allows fallback path resolution in hir::Semantics to properly identify our derives
103 let loc = db.lookup_intern_macro_call(id);
104 let derives = match &loc.kind {
105 MacroCallKind::Attr { attr_args, .. } => &attr_args.0,
106 _ => return ExpandResult::ok(tt.clone()),
109 let mut token_trees = Vec::new();
110 for (comma, group) in &derives
113 .filter_map(|tt| match tt {
114 tt::TokenTree::Leaf(l) => Some(l),
115 tt::TokenTree::Subtree(_) => None,
117 .group_by(|l| matches!(l, tt::Leaf::Punct(tt::Punct { char: ',', .. })))
122 let wrap = |leaf| tt::TokenTree::Leaf(tt::Leaf::Punct(leaf));
123 token_trees.push(wrap(tt::Punct {
125 spacing: tt::Spacing::Alone,
126 id: tt::TokenId::unspecified(),
128 token_trees.push(wrap(tt::Punct {
130 spacing: tt::Spacing::Alone,
131 id: tt::TokenId::unspecified(),
133 token_trees.extend(group.cloned().map(tt::TokenTree::Leaf));
134 token_trees.push(wrap(tt::Punct {
136 spacing: tt::Spacing::Alone,
137 id: tt::TokenId::unspecified(),
139 token_trees.push(wrap(tt::Punct {
141 spacing: tt::Spacing::Alone,
142 id: tt::TokenId::unspecified(),
144 token_trees.push(wrap(tt::Punct {
146 spacing: tt::Spacing::Alone,
147 id: tt::TokenId::unspecified(),
149 token_trees.push(wrap(tt::Punct {
151 spacing: tt::Spacing::Alone,
152 id: tt::TokenId::unspecified(),
155 ExpandResult::ok(tt::Subtree { delimiter: tt.delimiter, token_trees })