• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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