use core::fmt;
use std::any::Any;
use std::{panic::RefUnwindSafe, sync};
use base_db::{Crate, CrateBuilderId, CratesIdMap, Env};
use intern::Symbol;
use rustc_hash::FxHashMap;
use span::Span;
use triomphe::Arc;
use crate::{ExpandError, ExpandErrorKind, ExpandResult, db::ExpandDatabase, tt};
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Hash)]
pub enum ProcMacroKind {
CustomDerive,
Bang,
Attr,
}
pub trait AsAny: Any {
fn as_any(&self) -> &dyn Any;
}
impl<T: Any> AsAny for T {
fn as_any(&self) -> &dyn Any {
self
}
}
pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe + AsAny {
fn expand(
&self,
subtree: &tt::TopSubtree,
attrs: Option<&tt::TopSubtree>,
env: &Env,
def_site: Span,
call_site: Span,
mixed_site: Span,
current_dir: String,
) -> Result<tt::TopSubtree, ProcMacroExpansionError>;
fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool;
}
impl PartialEq for dyn ProcMacroExpander {
fn eq(&self, other: &Self) -> bool {
self.eq_dyn(other)
}
}
impl Eq for dyn ProcMacroExpander {}
#[derive(Debug)]
pub enum ProcMacroExpansionError {
Panic(String),
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<CrateBuilderId, Arc<CrateProcMacros>>);
impl ProcMacrosBuilder {
pub fn insert(
&mut self,
proc_macros_crate: CrateBuilderId,
mut proc_macro: ProcMacroLoadResult,
) {
if let Ok(proc_macros) = &mut proc_macro {
proc_macros.sort_unstable_by(|proc_macro, proc_macro2| {
(proc_macro.name.as_str(), proc_macro.kind)
.cmp(&(proc_macro2.name.as_str(), proc_macro2.kind))
});
}
self.0.insert(
proc_macros_crate,
match proc_macro {
Ok(it) => Arc::new(CrateProcMacros(Ok(it.into_boxed_slice()))),
Err((e, hard_err)) => {
Arc::new(CrateProcMacros(Err((e.into_boxed_str(), hard_err))))
}
},
);
}
pub(crate) fn build(self, crates_id_map: &CratesIdMap) -> ProcMacros {
let mut map = self
.0
.into_iter()
.map(|(krate, proc_macro)| (crates_id_map[&krate], proc_macro))
.collect::<FxHashMap<_, _>>();
map.shrink_to_fit();
ProcMacros(map)
}
}
impl FromIterator<(CrateBuilderId, ProcMacroLoadResult)> for ProcMacrosBuilder {
fn from_iter<T: IntoIterator<Item = (CrateBuilderId, ProcMacroLoadResult)>>(iter: T) -> Self {
let mut builder = ProcMacrosBuilder::default();
for (k, v) in iter {
builder.insert(k, v);
}
builder
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct CrateProcMacros(StoredProcMacroLoadResult);
#[derive(Default, Debug)]
pub struct ProcMacros(FxHashMap<Crate, Arc<CrateProcMacros>>);
impl ProcMacros {
fn get(&self, krate: Crate) -> Option<Arc<CrateProcMacros>> {
self.0.get(&krate).cloned()
}
}
impl CrateProcMacros {
fn get(&self, idx: u32, err_span: Span) -> Result<&ProcMacro, ExpandError> {
let proc_macros = match &self.0 {
Ok(proc_macros) => proc_macros,
Err(_) => {
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(&self) -> Option<(&str, bool)> {
self.0.as_ref().err().map(|(e, hard_err)| (&**e, *hard_err))
}
pub fn list(
&self,
def_site_ctx: span::SyntaxContext,
) -> Option<Box<[(crate::name::Name, CustomProcMacroExpander, bool)]>> {
match &self.0 {
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,
}
}
}
#[derive(Debug, Clone, Eq)]
pub struct ProcMacro {
pub name: Symbol,
pub kind: ProcMacroKind,
pub expander: sync::Arc<dyn ProcMacroExpander>,
pub disabled: bool,
}
impl PartialEq for ProcMacro {
fn eq(&self, other: &Self) -> bool {
let Self { name, kind, expander, disabled } = self;
let Self {
name: other_name,
kind: other_kind,
expander: other_expander,
disabled: other_disabled,
} = other;
name == other_name
&& kind == other_kind
&& expander == other_expander
&& disabled == other_disabled
}
}
#[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 }
}
pub const fn missing_expander() -> Self {
Self { proc_macro_id: Self::MISSING_EXPANDER }
}
pub const fn disabled() -> Self {
Self { proc_macro_id: Self::DISABLED_ID }
}
pub const fn disabled_proc_attr() -> Self {
Self { proc_macro_id: Self::PROC_MACRO_ATTR_DISABLED }
}
pub const fn is_missing(&self) -> bool {
self.proc_macro_id == Self::MISSING_EXPANDER
}
pub const fn is_disabled(&self) -> bool {
self.proc_macro_id == Self::DISABLED_ID
}
pub const fn is_disabled_proc_attr(&self) -> bool {
self.proc_macro_id == Self::PROC_MACRO_ATTR_DISABLED
}
pub fn as_expand_error(&self, def_crate: Crate) -> 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: Crate,
calling_crate: Crate,
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 = match db.proc_macros_for_crate(def_crate) {
Some(it) => it,
None => {
return ExpandResult::new(
tt::TopSubtree::empty(tt::DelimSpan {
open: call_site,
close: call_site,
}),
ExpandError::other(
call_site,
"internal error: no proc macros for crate",
),
);
}
};
let proc_macro = match proc_macros.get(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 env = calling_crate.env(db);
let current_dir = calling_crate.data(db).proc_macro_cwd.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 {
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()),
),
),
},
}
}
}
}
}
pub(crate) fn proc_macros_for_crate(
db: &dyn ExpandDatabase,
krate: Crate,
) -> Option<Arc<CrateProcMacros>> {
db.proc_macros().get(krate)
}