Skip to main content

hir_expand/
proc_macro.rs

1//! Proc Macro Expander stuff
2
3use 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
23/// A proc-macro expander implementation.
24pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe + Any {
25    /// Run the expander with the given input subtree, optional attribute input subtree (for
26    /// [`ProcMacroKind::Attr`]), environment variables, and span information.
27    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    /// The proc-macro panicked.
53    Panic(String),
54    /// The server itself errored out.
55    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            // Sort proc macros to improve incrementality when only their order has changed (ideally the build system
72            // will not change their order, but just to be sure).
73            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    /// Builds [`ProcMacros`] and adds id to `db`
88    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/// The proc macros. Do not use [`Self::get`]! Use [`Self::get_for_crate`] instead.
117#[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    /// Incrementality query to prevent queries from directly depending on [`Self::get`].
132    #[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    /// Fetch the [`CustomProcMacroExpander`]s and their corresponding names for the given crate.
166    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/// A loaded proc-macro.
187#[derive(Debug, Clone, Eq)]
188pub struct ProcMacro {
189    /// The name of the proc macro.
190    pub name: Symbol,
191    pub kind: ProcMacroKind,
192    /// The expander handle for this proc macro.
193    pub expander: sync::Arc<dyn ProcMacroExpander>,
194    /// Whether this proc-macro is disabled for early name resolution. Notably, the
195    /// [`Self::expander`] is still usable.
196    pub disabled: bool,
197}
198
199// `#[derive(PartialEq)]` generates a strange "cannot move" error.
200impl 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/// A custom proc-macro expander handle. This handle together with its crate resolves to a [`ProcMacro`]
217#[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    /// An expander that always errors due to the actual proc-macro expander missing.
235    pub const fn missing_expander() -> Self {
236        Self { proc_macro_id: Self::MISSING_EXPANDER }
237    }
238
239    /// A dummy expander that always errors. This expander is used for macros that have been disabled.
240    pub const fn disabled() -> Self {
241        Self { proc_macro_id: Self::DISABLED_ID }
242    }
243
244    /// A dummy expander that always errors. This expander is used for attribute macros when
245    /// proc-macro attribute expansion is disabled.
246    pub const fn disabled_proc_attr() -> Self {
247        Self { proc_macro_id: Self::PROC_MACRO_ATTR_DISABLED }
248    }
249
250    /// The macro-expander is missing or has yet to be build.
251    pub const fn is_missing(&self) -> bool {
252        self.proc_macro_id == Self::MISSING_EXPANDER
253    }
254
255    /// The macro is explicitly disabled and cannot be expanded.
256    pub const fn is_disabled(&self) -> bool {
257        self.proc_macro_id == Self::DISABLED_ID
258    }
259
260    /// The macro is explicitly disabled due to proc-macro attribute expansion being disabled.
261    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                // Proc macros have access to the environment variables of the invoking crate.
328                let env = calling_crate.env(db);
329                // FIXME: Can we avoid the string allocation here?
330                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                        // Don't discard the item in case something unexpected happened while expanding attributes
345                        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}