hir_def/nameres/
proc_macro.rs

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