1 //! Nameres-specific procedural macro data and helpers.
2
3 use hir_expand::name::{AsName, Name};
4
5 use crate::attr::Attrs;
6 use crate::tt::{Leaf, TokenTree};
7
8 #[derive(Debug, PartialEq, Eq)]
9 pub struct ProcMacroDef {
10 pub name: Name,
11 pub kind: ProcMacroKind,
12 }
13
14 #[derive(Debug, PartialEq, Eq)]
15 pub enum ProcMacroKind {
16 CustomDerive { helpers: Box<[Name]> },
17 FnLike,
18 Attr,
19 }
20
21 impl ProcMacroKind {
to_basedb_kind(&self) -> base_db::ProcMacroKind22 pub(super) fn to_basedb_kind(&self) -> base_db::ProcMacroKind {
23 match self {
24 ProcMacroKind::CustomDerive { .. } => base_db::ProcMacroKind::CustomDerive,
25 ProcMacroKind::FnLike => base_db::ProcMacroKind::FuncLike,
26 ProcMacroKind::Attr => base_db::ProcMacroKind::Attr,
27 }
28 }
29 }
30
31 impl Attrs {
32 #[rustfmt::skip]
parse_proc_macro_decl(&self, func_name: &Name) -> Option<ProcMacroDef>33 pub fn parse_proc_macro_decl(&self, func_name: &Name) -> Option<ProcMacroDef> {
34 if self.is_proc_macro() {
35 Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::FnLike })
36 } else if self.is_proc_macro_attribute() {
37 Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr })
38 } else if self.by_key("proc_macro_derive").exists() {
39 let derive = self.by_key("proc_macro_derive").tt_values().next()?;
40 let def = parse_macro_name_and_helper_attrs(&derive.token_trees)
41 .map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::CustomDerive { helpers } });
42
43 if def.is_none() {
44 tracing::trace!("malformed `#[proc_macro_derive]`: {}", derive);
45 }
46
47 def
48 } else {
49 None
50 }
51 }
52 }
53
54 // This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have
55 // the same structure.
56 #[rustfmt::skip]
parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Name, Box<[Name]>)>57 pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Name, Box<[Name]>)> {
58 match tt {
59 // `#[proc_macro_derive(Trait)]`
60 // `#[rustc_builtin_macro(Trait)]`
61 [TokenTree::Leaf(Leaf::Ident(trait_name))] => Some((trait_name.as_name(), Box::new([]))),
62
63 // `#[proc_macro_derive(Trait, attributes(helper1, helper2, ...))]`
64 // `#[rustc_builtin_macro(Trait, attributes(helper1, helper2, ...))]`
65 [
66 TokenTree::Leaf(Leaf::Ident(trait_name)),
67 TokenTree::Leaf(Leaf::Punct(comma)),
68 TokenTree::Leaf(Leaf::Ident(attributes)),
69 TokenTree::Subtree(helpers)
70 ] if comma.char == ',' && attributes.text == "attributes" =>
71 {
72 let helpers = helpers
73 .token_trees
74 .iter()
75 .filter(
76 |tt| !matches!(tt, TokenTree::Leaf(Leaf::Punct(comma)) if comma.char == ','),
77 )
78 .map(|tt| match tt {
79 TokenTree::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()),
80 _ => None,
81 })
82 .collect::<Option<Box<[_]>>>()?;
83
84 Some((trait_name.as_name(), helpers))
85 }
86
87 _ => None,
88 }
89 }
90