bon_macros/normalization/
generics_namespace.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
use crate::util::prelude::*;
use proc_macro2::TokenTree;
use std::collections::BTreeSet;
use syn::visit::Visit;

#[derive(Debug, Default, Clone)]
pub(crate) struct GenericsNamespace {
    /// Set of identifiers referenced in the syntax node.
    pub(crate) idents: BTreeSet<String>,

    /// Set of lifetimes referenced in the syntax node.
    pub(crate) lifetimes: BTreeSet<String>,
}

impl Visit<'_> for GenericsNamespace {
    fn visit_ident(&mut self, ident: &syn::Ident) {
        self.idents.insert(ident.to_string());
    }

    fn visit_meta_list(&mut self, meta_list: &syn::MetaList) {
        syn::visit::visit_meta_list(self, meta_list);
        self.visit_token_stream(meta_list.tokens.clone());
    }

    fn visit_lifetime(&mut self, lifetime: &syn::Lifetime) {
        self.lifetimes.insert(lifetime.ident.to_string());
    }

    fn visit_item(&mut self, _item: &syn::Item) {
        // Don't recurse into child items. They don't inherit the parent item's generics.
    }
}

impl GenericsNamespace {
    pub(crate) fn unique_ident(&self, name: String) -> syn::Ident {
        let name = Self::unique_name(&self.idents, name);
        syn::Ident::new(&name, Span::call_site())
    }

    pub(crate) fn unique_lifetime(&self, name: String) -> String {
        Self::unique_name(&self.lifetimes, name)
    }

    /// Adds `_` suffix to the name to avoid conflicts with existing identifiers.
    fn unique_name(taken: &BTreeSet<String>, mut ident: String) -> String {
        while taken.contains(&ident) {
            ident.push('_');
        }
        ident
    }

    pub(crate) fn visit_token_stream(&mut self, token_stream: TokenStream) {
        let mut tokens = token_stream.into_iter().peekable();
        while let Some(tt) = tokens.next() {
            match tt {
                TokenTree::Group(group) => {
                    self.visit_token_stream(group.stream());
                }
                TokenTree::Ident(ident) => {
                    self.visit_ident(&ident);
                }
                TokenTree::Punct(punct) => {
                    if punct.as_char() != '\'' {
                        continue;
                    }
                    if let Some(TokenTree::Ident(ident)) = tokens.peek() {
                        self.lifetimes.insert(ident.to_string());
                        tokens.next();
                    }
                }
                TokenTree::Literal(_) => {}
            }
        }
    }
}