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