1use base_db::{Crate, SourceDatabase};
4use hir_expand::{
5 AttrMacroAttrIds, MacroCallId, MacroCallKind, MacroDefId,
6 attrs::{Attr, AttrId, AttrInput},
7 inert_attr_macro::find_builtin_attr_idx,
8 mod_path::{ModPath, PathKind},
9};
10use span::SyntaxContext;
11use syntax::ast;
12
13use crate::{
14 AstIdWithPath, MacroId, ModuleId, UnresolvedMacro,
15 item_scope::BuiltinShadowMode,
16 nameres::{LocalDefMap, path_resolution::ResolveMode},
17};
18
19use super::{DefMap, MacroSubNs};
20
21pub enum ResolvedAttr {
22 Macro(MacroCallId),
24 Other,
26}
27
28impl DefMap {
29 pub(crate) fn resolve_attr_macro(
31 &self,
32 local_def_map: &LocalDefMap,
33 db: &dyn SourceDatabase,
34 original_module: ModuleId,
35 ast_id: AstIdWithPath<ast::Item>,
36 attr: &Attr,
37 attr_id: AttrId,
38 macro_depth: u32,
39 ) -> Result<ResolvedAttr, UnresolvedMacro> {
40 if self.is_builtin_or_registered_attr(&ast_id.path) {
43 return Ok(ResolvedAttr::Other);
44 }
45
46 let resolved_res = self.resolve_path_fp_with_macro(
47 local_def_map,
48 db,
49 ResolveMode::Other,
50 original_module,
51 &ast_id.path,
52 BuiltinShadowMode::Module,
53 Some(MacroSubNs::Attr),
54 );
55 let def = match resolved_res.resolved_def.take_macros() {
56 Some(def) => {
57 if def.is_attribute(db) {
60 def
61 } else {
62 return Ok(ResolvedAttr::Other);
63 }
64 }
65 None => return Err(UnresolvedMacro { path: (*ast_id.path).clone() }),
66 };
67
68 Ok(ResolvedAttr::Macro(attr_macro_as_call_id(
69 db,
70 &ast_id,
71 attr,
72 AttrMacroAttrIds::from_one(attr_id),
75 self.krate,
76 def.definition(db),
77 macro_depth,
78 )))
79 }
80
81 pub(crate) fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
82 if path.kind != PathKind::Plain {
83 return false;
84 }
85
86 let segments = path.segments();
87
88 if let Some(name) = segments.first() {
89 let name = name.symbol();
90 let pred = |n: &_| *n == *name;
91
92 let is_tool = self.data.registered_tools.iter().any(pred);
93 if is_tool {
95 return true;
96 }
97
98 if segments.len() == 1 && find_builtin_attr_idx(name).is_some() {
99 return true;
100 }
101 }
102 false
103 }
104}
105
106pub(super) fn attr_macro_as_call_id(
107 db: &dyn SourceDatabase,
108 item_attr: &AstIdWithPath<ast::Item>,
109 macro_attr: &Attr,
110 censored_attr_ids: AttrMacroAttrIds,
111 krate: Crate,
112 def: MacroDefId,
113 macro_depth: u32,
114) -> MacroCallId {
115 let arg = match macro_attr.input.as_deref() {
116 Some(AttrInput::TokenTree(tt)) => {
117 let mut tt = tt.clone();
118 tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
119 Some(tt)
120 }
121
122 _ => None,
123 };
124
125 def.make_call(
126 db,
127 krate,
128 MacroCallKind::Attr {
129 ast_id: item_attr.ast_id,
130 attr_args: arg.map(Box::new),
131 censored_attr_ids,
132 },
133 macro_attr.ctxt,
134 macro_depth,
135 )
136}
137
138pub(super) fn derive_macro_as_call_id(
139 db: &dyn SourceDatabase,
140 item_attr: &AstIdWithPath<ast::Adt>,
141 derive_attr_index: AttrId,
142 derive_pos: u32,
143 call_site: SyntaxContext,
144 krate: Crate,
145 resolver: impl Fn(&ModPath) -> Option<(MacroId, MacroDefId)>,
146 derive_macro_id: MacroCallId,
147 macro_depth: u32,
148) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
149 let (macro_id, def_id) = resolver(&item_attr.path)
150 .filter(|(_, def_id)| def_id.is_derive())
151 .ok_or_else(|| UnresolvedMacro { path: (*item_attr.path).clone() })?;
152 let call_id = def_id.make_call(
153 db,
154 krate,
155 MacroCallKind::Derive {
156 ast_id: item_attr.ast_id,
157 derive_index: derive_pos,
158 derive_attr_index,
159 derive_macro_id,
160 },
161 call_site,
162 macro_depth,
163 );
164 Ok((macro_id, def_id, call_id))
165}