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