bon_macros/normalization/
generics_namespace.rs

1use crate::util::prelude::*;
2use proc_macro2::TokenTree;
3use std::collections::BTreeSet;
4use syn::visit::Visit;
5
6#[derive(Debug, Default, Clone)]
7pub(crate) struct GenericsNamespace {
8    /// Set of identifiers referenced in the syntax node.
9    pub(crate) idents: BTreeSet<String>,
10
11    /// Set of lifetimes referenced in the syntax node.
12    pub(crate) lifetimes: BTreeSet<String>,
13}
14
15impl Visit<'_> for GenericsNamespace {
16    fn visit_ident(&mut self, ident: &syn::Ident) {
17        self.idents.insert(ident.to_string());
18    }
19
20    fn visit_meta_list(&mut self, meta_list: &syn::MetaList) {
21        syn::visit::visit_meta_list(self, meta_list);
22        self.visit_token_stream(meta_list.tokens.clone());
23    }
24
25    fn visit_lifetime(&mut self, lifetime: &syn::Lifetime) {
26        self.lifetimes.insert(lifetime.ident.to_string());
27    }
28
29    fn visit_item(&mut self, _item: &syn::Item) {
30        // Don't recurse into child items. They don't inherit the parent item's generics.
31    }
32}
33
34impl GenericsNamespace {
35    pub(crate) fn unique_ident(&self, name: String) -> syn::Ident {
36        let name = Self::unique_name(&self.idents, name);
37        syn::Ident::new(&name, Span::call_site())
38    }
39
40    pub(crate) fn unique_lifetime(&self, name: String) -> String {
41        Self::unique_name(&self.lifetimes, name)
42    }
43
44    /// Adds `_` suffix to the name to avoid conflicts with existing identifiers.
45    fn unique_name(taken: &BTreeSet<String>, mut ident: String) -> String {
46        while taken.contains(&ident) {
47            ident.push('_');
48        }
49        ident
50    }
51
52    pub(crate) fn visit_token_stream(&mut self, token_stream: TokenStream) {
53        let mut tokens = token_stream.into_iter().peekable();
54        while let Some(tt) = tokens.next() {
55            match tt {
56                TokenTree::Group(group) => {
57                    self.visit_token_stream(group.stream());
58                }
59                TokenTree::Ident(ident) => {
60                    self.visit_ident(&ident);
61                }
62                TokenTree::Punct(punct) => {
63                    if punct.as_char() != '\'' {
64                        continue;
65                    }
66                    if let Some(TokenTree::Ident(ident)) = tokens.peek() {
67                        self.lifetimes.insert(ident.to_string());
68                        tokens.next();
69                    }
70                }
71                TokenTree::Literal(_) => {}
72            }
73        }
74    }
75}