Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/nameres/proc_macro.rs')
-rw-r--r--crates/hir-def/src/nameres/proc_macro.rs81
1 files changed, 81 insertions, 0 deletions
diff --git a/crates/hir-def/src/nameres/proc_macro.rs b/crates/hir-def/src/nameres/proc_macro.rs
new file mode 100644
index 0000000000..5089ef2d81
--- /dev/null
+++ b/crates/hir-def/src/nameres/proc_macro.rs
@@ -0,0 +1,81 @@
+//! Nameres-specific procedural macro data and helpers.
+
+use hir_expand::name::{AsName, Name};
+use tt::{Leaf, TokenTree};
+
+use crate::attr::Attrs;
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct ProcMacroDef {
+ pub name: Name,
+ pub kind: ProcMacroKind,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum ProcMacroKind {
+ CustomDerive { helpers: Box<[Name]> },
+ FnLike,
+ Attr,
+}
+
+impl ProcMacroKind {
+ pub(super) fn to_basedb_kind(&self) -> base_db::ProcMacroKind {
+ match self {
+ ProcMacroKind::CustomDerive { .. } => base_db::ProcMacroKind::CustomDerive,
+ ProcMacroKind::FnLike => base_db::ProcMacroKind::FuncLike,
+ ProcMacroKind::Attr => base_db::ProcMacroKind::Attr,
+ }
+ }
+}
+
+impl Attrs {
+ #[rustfmt::skip]
+ pub fn parse_proc_macro_decl(&self, func_name: &Name) -> Option<ProcMacroDef> {
+ if self.is_proc_macro() {
+ Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::FnLike })
+ } else if self.is_proc_macro_attribute() {
+ Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr })
+ } else if self.by_key("proc_macro_derive").exists() {
+ let derive = self.by_key("proc_macro_derive").tt_values().next()?;
+
+ match &*derive.token_trees {
+ // `#[proc_macro_derive(Trait)]`
+ [TokenTree::Leaf(Leaf::Ident(trait_name))] => Some(ProcMacroDef {
+ name: trait_name.as_name(),
+ kind: ProcMacroKind::CustomDerive { helpers: Box::new([]) },
+ }),
+
+ // `#[proc_macro_derive(Trait, attibutes(helper1, helper2, ...))]`
+ [
+ TokenTree::Leaf(Leaf::Ident(trait_name)),
+ TokenTree::Leaf(Leaf::Punct(comma)),
+ TokenTree::Leaf(Leaf::Ident(attributes)),
+ TokenTree::Subtree(helpers)
+ ] if comma.char == ',' && attributes.text == "attributes" =>
+ {
+ let helpers = helpers.token_trees.iter()
+ .filter(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Punct(comma)) if comma.char == ','))
+ .map(|tt| {
+ match tt {
+ TokenTree::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()),
+ _ => None
+ }
+ })
+ .collect::<Option<Box<[_]>>>()?;
+
+ Some(ProcMacroDef {
+ name: trait_name.as_name(),
+ kind: ProcMacroKind::CustomDerive { helpers },
+ })
+ }
+
+ _ => {
+ tracing::trace!("malformed `#[proc_macro_derive]`: {}", derive);
+ None
+ }
+ }
+ } else {
+ None
+ }
+ }
+}