]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
:arrow_up: rust-analyzer
[rust.git] / src / tools / rust-analyzer / crates / hir-def / src / nameres / proc_macro.rs
1 //! Nameres-specific procedural macro data and helpers.
2
3 use hir_expand::name::{AsName, Name};
4 use tt::{Leaf, TokenTree};
5
6 use crate::attr::Attrs;
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 {
22     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]
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
41             match &*derive.token_trees {
42                 // `#[proc_macro_derive(Trait)]`
43                 [TokenTree::Leaf(Leaf::Ident(trait_name))] => Some(ProcMacroDef {
44                     name: trait_name.as_name(),
45                     kind: ProcMacroKind::CustomDerive { helpers: Box::new([]) },
46                 }),
47
48                 // `#[proc_macro_derive(Trait, attributes(helper1, helper2, ...))]`
49                 [
50                     TokenTree::Leaf(Leaf::Ident(trait_name)),
51                     TokenTree::Leaf(Leaf::Punct(comma)),
52                     TokenTree::Leaf(Leaf::Ident(attributes)),
53                     TokenTree::Subtree(helpers)
54                 ] if comma.char == ',' && attributes.text == "attributes" =>
55                 {
56                     let helpers = helpers.token_trees.iter()
57                         .filter(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Punct(comma)) if comma.char == ','))
58                         .map(|tt| {
59                             match tt {
60                                 TokenTree::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()),
61                                 _ => None
62                             }
63                         })
64                         .collect::<Option<Box<[_]>>>()?;
65
66                     Some(ProcMacroDef {
67                         name: trait_name.as_name(),
68                         kind: ProcMacroKind::CustomDerive { helpers },
69                     })
70                 }
71
72                 _ => {
73                     tracing::trace!("malformed `#[proc_macro_derive]`: {}", derive);
74                     None
75                 }
76             }
77         } else {
78             None
79         }
80     }
81 }