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 = 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        unique_name(&self.lifetimes, name)
42    }
43
44    pub(crate) fn visit_token_stream(&mut self, token_stream: TokenStream) {
45        let mut tokens = token_stream.into_iter().peekable();
46        while let Some(tt) = tokens.next() {
47            match tt {
48                TokenTree::Group(group) => {
49                    self.visit_token_stream(group.stream());
50                }
51                TokenTree::Ident(ident) => {
52                    self.visit_ident(&ident);
53                }
54                TokenTree::Punct(punct) => {
55                    if punct.as_char() != '\'' {
56                        continue;
57                    }
58                    if let Some(TokenTree::Ident(ident)) = tokens.peek() {
59                        self.lifetimes.insert(ident.to_string());
60                        tokens.next();
61                    }
62                }
63                TokenTree::Literal(_) => {}
64            }
65        }
66    }
67}
68
69/// Adds `_` suffix to the name to avoid conflicts with existing identifiers.
70pub(crate) fn unique_name(taken: &BTreeSet<String>, mut ident: String) -> String {
71    while taken.contains(&ident) {
72        ident.push('_');
73    }
74    ident
75}