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