| //! Proc Macro Expander stuff |
| |
| use core::fmt; |
| use std::{panic::RefUnwindSafe, sync}; |
| |
| use base_db::{CrateId, Env}; |
| use intern::Symbol; |
| use rustc_hash::FxHashMap; |
| use span::Span; |
| |
| use crate::{db::ExpandDatabase, tt, ExpandError, ExpandErrorKind, ExpandResult}; |
| |
| #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] |
| pub enum ProcMacroKind { |
| CustomDerive, |
| Bang, |
| Attr, |
| } |
| |
| /// A proc-macro expander implementation. |
| pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe { |
| /// Run the expander with the given input subtree, optional attribute input subtree (for |
| /// [`ProcMacroKind::Attr`]), environment variables, and span information. |
| fn expand( |
| &self, |
| subtree: &tt::TopSubtree, |
| attrs: Option<&tt::TopSubtree>, |
| env: &Env, |
| def_site: Span, |
| call_site: Span, |
| mixed_site: Span, |
| current_dir: Option<String>, |
| ) -> Result<tt::TopSubtree, ProcMacroExpansionError>; |
| } |
| |
| #[derive(Debug)] |
| pub enum ProcMacroExpansionError { |
| /// The proc-macro panicked. |
| Panic(String), |
| /// The server itself errored out. |
| System(String), |
| } |
| |
| pub type ProcMacroLoadResult = Result<Vec<ProcMacro>, (String, bool)>; |
| type StoredProcMacroLoadResult = Result<Box<[ProcMacro]>, (Box<str>, bool)>; |
| |
| #[derive(Default, Debug)] |
| pub struct ProcMacrosBuilder(FxHashMap<CrateId, StoredProcMacroLoadResult>); |
| impl ProcMacrosBuilder { |
| pub fn insert(&mut self, proc_macros_crate: CrateId, proc_macro: ProcMacroLoadResult) { |
| self.0.insert( |
| proc_macros_crate, |
| match proc_macro { |
| Ok(it) => Ok(it.into_boxed_slice()), |
| Err((e, hard_err)) => Err((e.into_boxed_str(), hard_err)), |
| }, |
| ); |
| } |
| pub fn build(mut self) -> ProcMacros { |
| self.0.shrink_to_fit(); |
| ProcMacros(self.0) |
| } |
| } |
| |
| #[derive(Default, Debug)] |
| pub struct ProcMacros(FxHashMap<CrateId, StoredProcMacroLoadResult>); |
| |
| impl FromIterator<(CrateId, ProcMacroLoadResult)> for ProcMacros { |
| fn from_iter<T: IntoIterator<Item = (CrateId, ProcMacroLoadResult)>>(iter: T) -> Self { |
| let mut builder = ProcMacrosBuilder::default(); |
| for (k, v) in iter { |
| builder.insert(k, v); |
| } |
| builder.build() |
| } |
| } |
| |
| impl ProcMacros { |
| fn get(&self, krate: CrateId, idx: u32, err_span: Span) -> Result<&ProcMacro, ExpandError> { |
| let proc_macros = match self.0.get(&krate) { |
| Some(Ok(proc_macros)) => proc_macros, |
| Some(Err(_)) | None => { |
| return Err(ExpandError::other( |
| err_span, |
| "internal error: no proc macros for crate", |
| )); |
| } |
| }; |
| proc_macros.get(idx as usize).ok_or_else(|| { |
| ExpandError::other(err_span, |
| format!( |
| "internal error: proc-macro index out of bounds: the length is {} but the index is {}", |
| proc_macros.len(), |
| idx |
| ) |
| ) |
| } |
| ) |
| } |
| |
| pub fn get_error_for_crate(&self, krate: CrateId) -> Option<(&str, bool)> { |
| self.0.get(&krate).and_then(|it| it.as_ref().err()).map(|(e, hard_err)| (&**e, *hard_err)) |
| } |
| |
| /// Fetch the [`CustomProcMacroExpander`]s and their corresponding names for the given crate. |
| pub fn for_crate( |
| &self, |
| krate: CrateId, |
| def_site_ctx: span::SyntaxContextId, |
| ) -> Option<Box<[(crate::name::Name, CustomProcMacroExpander, bool)]>> { |
| match self.0.get(&krate) { |
| Some(Ok(proc_macros)) => Some({ |
| proc_macros |
| .iter() |
| .enumerate() |
| .map(|(idx, it)| { |
| let name = crate::name::Name::new_symbol(it.name.clone(), def_site_ctx); |
| (name, CustomProcMacroExpander::new(idx as u32), it.disabled) |
| }) |
| .collect() |
| }), |
| _ => None, |
| } |
| } |
| } |
| |
| /// A loaded proc-macro. |
| #[derive(Debug, Clone)] |
| pub struct ProcMacro { |
| /// The name of the proc macro. |
| pub name: Symbol, |
| pub kind: ProcMacroKind, |
| /// The expander handle for this proc macro. |
| pub expander: sync::Arc<dyn ProcMacroExpander>, |
| /// Whether this proc-macro is disabled for early name resolution. Notably, the |
| /// [`Self::expander`] is still usable. |
| pub disabled: bool, |
| } |
| |
| /// A custom proc-macro expander handle. This handle together with its crate resolves to a [`ProcMacro`] |
| #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] |
| pub struct CustomProcMacroExpander { |
| proc_macro_id: u32, |
| } |
| |
| impl CustomProcMacroExpander { |
| const MISSING_EXPANDER: u32 = !0; |
| const DISABLED_ID: u32 = !1; |
| const PROC_MACRO_ATTR_DISABLED: u32 = !2; |
| |
| pub fn new(proc_macro_id: u32) -> Self { |
| assert_ne!(proc_macro_id, Self::MISSING_EXPANDER); |
| assert_ne!(proc_macro_id, Self::DISABLED_ID); |
| assert_ne!(proc_macro_id, Self::PROC_MACRO_ATTR_DISABLED); |
| Self { proc_macro_id } |
| } |
| |
| /// An expander that always errors due to the actual proc-macro expander missing. |
| pub const fn missing_expander() -> Self { |
| Self { proc_macro_id: Self::MISSING_EXPANDER } |
| } |
| |
| /// A dummy expander that always errors. This expander is used for macros that have been disabled. |
| pub const fn disabled() -> Self { |
| Self { proc_macro_id: Self::DISABLED_ID } |
| } |
| |
| /// A dummy expander that always errors. This expander is used for attribute macros when |
| /// proc-macro attribute expansion is disabled. |
| pub const fn disabled_proc_attr() -> Self { |
| Self { proc_macro_id: Self::PROC_MACRO_ATTR_DISABLED } |
| } |
| |
| /// The macro-expander is missing or has yet to be build. |
| pub const fn is_missing(&self) -> bool { |
| self.proc_macro_id == Self::MISSING_EXPANDER |
| } |
| |
| /// The macro is explicitly disabled and cannot be expanded. |
| pub const fn is_disabled(&self) -> bool { |
| self.proc_macro_id == Self::DISABLED_ID |
| } |
| |
| /// The macro is explicitly disabled due to proc-macro attribute expansion being disabled. |
| pub const fn is_disabled_proc_attr(&self) -> bool { |
| self.proc_macro_id == Self::PROC_MACRO_ATTR_DISABLED |
| } |
| |
| /// The macro is explicitly disabled due to proc-macro attribute expansion being disabled. |
| pub fn as_expand_error(&self, def_crate: CrateId) -> Option<ExpandErrorKind> { |
| match self.proc_macro_id { |
| Self::PROC_MACRO_ATTR_DISABLED => Some(ExpandErrorKind::ProcMacroAttrExpansionDisabled), |
| Self::DISABLED_ID => Some(ExpandErrorKind::MacroDisabled), |
| Self::MISSING_EXPANDER => Some(ExpandErrorKind::MissingProcMacroExpander(def_crate)), |
| _ => None, |
| } |
| } |
| |
| pub fn expand( |
| self, |
| db: &dyn ExpandDatabase, |
| def_crate: CrateId, |
| calling_crate: CrateId, |
| tt: &tt::TopSubtree, |
| attr_arg: Option<&tt::TopSubtree>, |
| def_site: Span, |
| call_site: Span, |
| mixed_site: Span, |
| ) -> ExpandResult<tt::TopSubtree> { |
| match self.proc_macro_id { |
| Self::PROC_MACRO_ATTR_DISABLED => ExpandResult::new( |
| tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }), |
| ExpandError::new(call_site, ExpandErrorKind::ProcMacroAttrExpansionDisabled), |
| ), |
| Self::MISSING_EXPANDER => ExpandResult::new( |
| tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }), |
| ExpandError::new(call_site, ExpandErrorKind::MissingProcMacroExpander(def_crate)), |
| ), |
| Self::DISABLED_ID => ExpandResult::new( |
| tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }), |
| ExpandError::new(call_site, ExpandErrorKind::MacroDisabled), |
| ), |
| id => { |
| let proc_macros = db.proc_macros(); |
| let proc_macro = match proc_macros.get(def_crate, id, call_site) { |
| Ok(proc_macro) => proc_macro, |
| Err(e) => { |
| return ExpandResult::new( |
| tt::TopSubtree::empty(tt::DelimSpan { |
| open: call_site, |
| close: call_site, |
| }), |
| e, |
| ) |
| } |
| }; |
| |
| let krate_graph = db.crate_graph(); |
| // Proc macros have access to the environment variables of the invoking crate. |
| let env = &krate_graph[calling_crate].env; |
| let current_dir = |
| krate_graph[calling_crate].proc_macro_cwd.as_deref().map(ToString::to_string); |
| |
| match proc_macro.expander.expand( |
| tt, |
| attr_arg, |
| env, |
| def_site, |
| call_site, |
| mixed_site, |
| current_dir, |
| ) { |
| Ok(t) => ExpandResult::ok(t), |
| Err(err) => match err { |
| // Don't discard the item in case something unexpected happened while expanding attributes |
| ProcMacroExpansionError::System(text) |
| if proc_macro.kind == ProcMacroKind::Attr => |
| { |
| ExpandResult { |
| value: tt.clone(), |
| err: Some(ExpandError::other(call_site, text)), |
| } |
| } |
| ProcMacroExpansionError::System(text) |
| | ProcMacroExpansionError::Panic(text) => ExpandResult::new( |
| tt::TopSubtree::empty(tt::DelimSpan { |
| open: call_site, |
| close: call_site, |
| }), |
| ExpandError::new( |
| call_site, |
| ExpandErrorKind::ProcMacroPanic(text.into_boxed_str()), |
| ), |
| ), |
| }, |
| } |
| } |
| } |
| } |
| } |