1use core::fmt;
4use std::any::Any;
5use std::{panic::RefUnwindSafe, sync};
6
7use base_db::{Crate, CrateBuilderId, CratesIdMap, Env, ProcMacroLoadingError};
8use intern::Symbol;
9use rustc_hash::FxHashMap;
10use span::Span;
11use triomphe::Arc;
12
13use crate::{ExpandError, ExpandErrorKind, ExpandResult, db::ExpandDatabase, tt};
14
15#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Hash)]
16pub enum ProcMacroKind {
17 CustomDerive,
18 Bang,
19 Attr,
20}
21
22pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe + Any {
24 fn expand(
27 &self,
28 subtree: &tt::TopSubtree,
29 attrs: Option<&tt::TopSubtree>,
30 env: &Env,
31 def_site: Span,
32 call_site: Span,
33 mixed_site: Span,
34 current_dir: String,
35 ) -> Result<tt::TopSubtree, ProcMacroExpansionError>;
36
37 fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool;
38}
39
40impl PartialEq for dyn ProcMacroExpander {
41 fn eq(&self, other: &Self) -> bool {
42 self.eq_dyn(other)
43 }
44}
45
46impl Eq for dyn ProcMacroExpander {}
47
48#[derive(Debug)]
49pub enum ProcMacroExpansionError {
50 Panic(String),
52 System(String),
54}
55
56pub type ProcMacroLoadResult = Result<Vec<ProcMacro>, ProcMacroLoadingError>;
57type StoredProcMacroLoadResult = Result<Box<[ProcMacro]>, ProcMacroLoadingError>;
58
59#[derive(Default, Debug)]
60pub struct ProcMacrosBuilder(FxHashMap<CrateBuilderId, Arc<CrateProcMacros>>);
61
62impl ProcMacrosBuilder {
63 pub fn insert(
64 &mut self,
65 proc_macros_crate: CrateBuilderId,
66 mut proc_macro: ProcMacroLoadResult,
67 ) {
68 if let Ok(proc_macros) = &mut proc_macro {
69 proc_macros.sort_unstable_by(|proc_macro, proc_macro2| {
72 (proc_macro.name.as_str(), proc_macro.kind)
73 .cmp(&(proc_macro2.name.as_str(), proc_macro2.kind))
74 });
75 }
76 self.0.insert(
77 proc_macros_crate,
78 match proc_macro {
79 Ok(it) => Arc::new(CrateProcMacros(Ok(it.into_boxed_slice()))),
80 Err(e) => Arc::new(CrateProcMacros(Err(e))),
81 },
82 );
83 }
84
85 pub(crate) fn build(self, crates_id_map: &CratesIdMap) -> ProcMacros {
86 let mut map = self
87 .0
88 .into_iter()
89 .map(|(krate, proc_macro)| (crates_id_map[&krate], proc_macro))
90 .collect::<FxHashMap<_, _>>();
91 map.shrink_to_fit();
92 ProcMacros(map)
93 }
94}
95
96impl FromIterator<(CrateBuilderId, ProcMacroLoadResult)> for ProcMacrosBuilder {
97 fn from_iter<T: IntoIterator<Item = (CrateBuilderId, ProcMacroLoadResult)>>(iter: T) -> Self {
98 let mut builder = ProcMacrosBuilder::default();
99 for (k, v) in iter {
100 builder.insert(k, v);
101 }
102 builder
103 }
104}
105
106#[derive(Debug, PartialEq, Eq)]
107pub struct CrateProcMacros(StoredProcMacroLoadResult);
108
109#[derive(Default, Debug)]
110pub struct ProcMacros(FxHashMap<Crate, Arc<CrateProcMacros>>);
111impl ProcMacros {
112 fn get(&self, krate: Crate) -> Option<Arc<CrateProcMacros>> {
113 self.0.get(&krate).cloned()
114 }
115}
116
117impl CrateProcMacros {
118 fn get(&self, idx: u32, err_span: Span) -> Result<&ProcMacro, ExpandError> {
119 let proc_macros = match &self.0 {
120 Ok(proc_macros) => proc_macros,
121 Err(_) => {
122 return Err(ExpandError::other(
123 err_span,
124 "internal error: no proc macros for crate",
125 ));
126 }
127 };
128 proc_macros.get(idx as usize).ok_or_else(|| {
129 ExpandError::other(err_span,
130 format!(
131 "internal error: proc-macro index out of bounds: the length is {} but the index is {}",
132 proc_macros.len(),
133 idx
134 )
135 )
136 }
137 )
138 }
139
140 pub fn get_error(&self) -> Option<&ProcMacroLoadingError> {
141 self.0.as_ref().err()
142 }
143
144 pub fn list(
146 &self,
147 def_site_ctx: span::SyntaxContext,
148 ) -> Option<Box<[(crate::name::Name, CustomProcMacroExpander, bool)]>> {
149 match &self.0 {
150 Ok(proc_macros) => Some(
151 proc_macros
152 .iter()
153 .enumerate()
154 .map(|(idx, it)| {
155 let name = crate::name::Name::new_symbol(it.name.clone(), def_site_ctx);
156 (name, CustomProcMacroExpander::new(idx as u32), it.disabled)
157 })
158 .collect(),
159 ),
160 _ => None,
161 }
162 }
163}
164
165#[derive(Debug, Clone, Eq)]
167pub struct ProcMacro {
168 pub name: Symbol,
170 pub kind: ProcMacroKind,
171 pub expander: sync::Arc<dyn ProcMacroExpander>,
173 pub disabled: bool,
176}
177
178impl PartialEq for ProcMacro {
180 fn eq(&self, other: &Self) -> bool {
181 let Self { name, kind, expander, disabled } = self;
182 let Self {
183 name: other_name,
184 kind: other_kind,
185 expander: other_expander,
186 disabled: other_disabled,
187 } = other;
188 name == other_name
189 && kind == other_kind
190 && expander == other_expander
191 && disabled == other_disabled
192 }
193}
194
195#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
197pub struct CustomProcMacroExpander {
198 proc_macro_id: u32,
199}
200
201impl CustomProcMacroExpander {
202 const MISSING_EXPANDER: u32 = !0;
203 const DISABLED_ID: u32 = !1;
204 const PROC_MACRO_ATTR_DISABLED: u32 = !2;
205
206 pub fn new(proc_macro_id: u32) -> Self {
207 assert_ne!(proc_macro_id, Self::MISSING_EXPANDER);
208 assert_ne!(proc_macro_id, Self::DISABLED_ID);
209 assert_ne!(proc_macro_id, Self::PROC_MACRO_ATTR_DISABLED);
210 Self { proc_macro_id }
211 }
212
213 pub const fn missing_expander() -> Self {
215 Self { proc_macro_id: Self::MISSING_EXPANDER }
216 }
217
218 pub const fn disabled() -> Self {
220 Self { proc_macro_id: Self::DISABLED_ID }
221 }
222
223 pub const fn disabled_proc_attr() -> Self {
226 Self { proc_macro_id: Self::PROC_MACRO_ATTR_DISABLED }
227 }
228
229 pub const fn is_missing(&self) -> bool {
231 self.proc_macro_id == Self::MISSING_EXPANDER
232 }
233
234 pub const fn is_disabled(&self) -> bool {
236 self.proc_macro_id == Self::DISABLED_ID
237 }
238
239 pub const fn is_disabled_proc_attr(&self) -> bool {
241 self.proc_macro_id == Self::PROC_MACRO_ATTR_DISABLED
242 }
243
244 pub fn as_expand_error(&self, def_crate: Crate) -> Option<ExpandErrorKind> {
245 match self.proc_macro_id {
246 Self::PROC_MACRO_ATTR_DISABLED => Some(ExpandErrorKind::ProcMacroAttrExpansionDisabled),
247 Self::DISABLED_ID => Some(ExpandErrorKind::MacroDisabled),
248 Self::MISSING_EXPANDER => Some(ExpandErrorKind::MissingProcMacroExpander(def_crate)),
249 _ => None,
250 }
251 }
252
253 pub fn expand(
254 self,
255 db: &dyn ExpandDatabase,
256 def_crate: Crate,
257 calling_crate: Crate,
258 tt: &tt::TopSubtree,
259 attr_arg: Option<&tt::TopSubtree>,
260 def_site: Span,
261 call_site: Span,
262 mixed_site: Span,
263 ) -> ExpandResult<tt::TopSubtree> {
264 match self.proc_macro_id {
265 Self::PROC_MACRO_ATTR_DISABLED => ExpandResult::new(
266 tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
267 ExpandError::new(call_site, ExpandErrorKind::ProcMacroAttrExpansionDisabled),
268 ),
269 Self::MISSING_EXPANDER => ExpandResult::new(
270 tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
271 ExpandError::new(call_site, ExpandErrorKind::MissingProcMacroExpander(def_crate)),
272 ),
273 Self::DISABLED_ID => ExpandResult::new(
274 tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
275 ExpandError::new(call_site, ExpandErrorKind::MacroDisabled),
276 ),
277 id => {
278 let proc_macros = match db.proc_macros_for_crate(def_crate) {
279 Some(it) => it,
280 None => {
281 return ExpandResult::new(
282 tt::TopSubtree::empty(tt::DelimSpan {
283 open: call_site,
284 close: call_site,
285 }),
286 ExpandError::other(
287 call_site,
288 "internal error: no proc macros for crate",
289 ),
290 );
291 }
292 };
293 let proc_macro = match proc_macros.get(id, call_site) {
294 Ok(proc_macro) => proc_macro,
295 Err(e) => {
296 return ExpandResult::new(
297 tt::TopSubtree::empty(tt::DelimSpan {
298 open: call_site,
299 close: call_site,
300 }),
301 e,
302 );
303 }
304 };
305
306 let env = calling_crate.env(db);
308 let current_dir = calling_crate.data(db).proc_macro_cwd.to_string();
310
311 match proc_macro.expander.expand(
312 tt,
313 attr_arg,
314 env,
315 def_site,
316 call_site,
317 mixed_site,
318 current_dir,
319 ) {
320 Ok(t) => ExpandResult::ok(t),
321 Err(err) => match err {
322 ProcMacroExpansionError::System(text)
324 if proc_macro.kind == ProcMacroKind::Attr =>
325 {
326 ExpandResult {
327 value: tt.clone(),
328 err: Some(ExpandError::other(call_site, text)),
329 }
330 }
331 ProcMacroExpansionError::System(text)
332 | ProcMacroExpansionError::Panic(text) => ExpandResult::new(
333 tt::TopSubtree::empty(tt::DelimSpan {
334 open: call_site,
335 close: call_site,
336 }),
337 ExpandError::new(
338 call_site,
339 ExpandErrorKind::ProcMacroPanic(text.into_boxed_str()),
340 ),
341 ),
342 },
343 }
344 }
345 }
346 }
347}
348
349pub(crate) fn proc_macros_for_crate(
350 db: &dyn ExpandDatabase,
351 krate: Crate,
352) -> Option<Arc<CrateProcMacros>> {
353 db.proc_macros().get(krate)
354}