Skip to main content

hir_def/nameres/
proc_macro.rs

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