rocket_codegen/
syn_ext.rs

1//! Extensions to `syn` types.
2
3use std::ops::Deref;
4use std::hash::{Hash, Hasher};
5use std::borrow::Cow;
6
7use syn::{self, Ident, ext::IdentExt as _, visit::Visit};
8use proc_macro2::{Span, TokenStream};
9use devise::ext::{PathExt, TypeExt as _};
10use rocket_http::ext::IntoOwned;
11
12pub trait IdentExt {
13    fn prepend(&self, string: &str) -> syn::Ident;
14    fn append(&self, string: &str) -> syn::Ident;
15    fn with_span(self, span: Span) -> syn::Ident;
16    fn rocketized(&self) -> syn::Ident;
17    fn uniqueify_with<F: FnMut(&mut dyn Hasher)>(&self, f: F) -> syn::Ident;
18}
19
20pub trait ReturnTypeExt {
21    fn ty(&self) -> Option<&syn::Type>;
22}
23
24pub trait TokenStreamExt {
25    fn respanned(&self, span: Span) -> Self;
26}
27
28pub trait FnArgExt {
29    fn typed(&self) -> Option<(&syn::Ident, &syn::Type)>;
30    fn wild(&self) -> Option<&syn::PatWild>;
31}
32
33pub trait TypeExt {
34    fn unfold(&self) -> Vec<Child<'_>>;
35    fn unfold_with_ty_macros(&self, names: &[&str], mapper: MacTyMapFn) -> Vec<Child<'_>>;
36    fn is_concrete(&self, generic_ident: &[&Ident]) -> bool;
37}
38
39pub trait GenericsExt {
40    fn type_idents(&self) -> Vec<&Ident>;
41}
42
43#[derive(Debug)]
44pub struct Child<'a> {
45    pub parent: Option<Cow<'a, syn::Type>>,
46    pub ty: Cow<'a, syn::Type>,
47}
48
49impl Deref for Child<'_> {
50    type Target = syn::Type;
51
52    fn deref(&self) -> &Self::Target {
53        &self.ty
54    }
55}
56
57impl IntoOwned for Child<'_> {
58    type Owned = Child<'static>;
59
60    fn into_owned(self) -> Self::Owned {
61        Child {
62            parent: self.parent.into_owned(),
63            ty: Cow::Owned(self.ty.into_owned()),
64        }
65    }
66}
67
68type MacTyMapFn = fn(&TokenStream) -> Option<syn::Type>;
69
70impl IdentExt for syn::Ident {
71    fn prepend(&self, string: &str) -> syn::Ident {
72        syn::Ident::new(&format!("{}{}", string, self.unraw()), self.span())
73    }
74
75    fn append(&self, string: &str) -> syn::Ident {
76        syn::Ident::new(&format!("{}{}", self, string), self.span())
77    }
78
79    fn with_span(mut self, span: Span) -> syn::Ident {
80        self.set_span(span);
81        self
82    }
83
84    fn rocketized(&self) -> syn::Ident {
85        self.prepend(crate::ROCKET_IDENT_PREFIX)
86    }
87
88    fn uniqueify_with<F: FnMut(&mut dyn Hasher)>(&self, mut f: F) -> syn::Ident {
89        use std::sync::atomic::{AtomicUsize, Ordering};
90        use std::collections::hash_map::DefaultHasher;
91
92        // Keep a global counter (+ thread ID later) to generate unique ids.
93        static COUNTER: AtomicUsize = AtomicUsize::new(0);
94
95        let mut hasher = DefaultHasher::new();
96        self.hash(&mut hasher);
97        std::process::id().hash(&mut hasher);
98        std::thread::current().id().hash(&mut hasher);
99        COUNTER.fetch_add(1, Ordering::AcqRel).hash(&mut hasher);
100        f(&mut hasher);
101
102        self.append(&format!("_{}", hasher.finish()))
103    }
104}
105
106impl ReturnTypeExt for syn::ReturnType {
107    fn ty(&self) -> Option<&syn::Type> {
108        match self {
109            syn::ReturnType::Default => None,
110            syn::ReturnType::Type(_, ty) => Some(ty),
111        }
112    }
113}
114
115impl TokenStreamExt for TokenStream {
116    fn respanned(&self, span: Span) -> Self {
117        self.clone().into_iter().map(|mut token| {
118            token.set_span(span);
119            token
120        }).collect()
121    }
122}
123
124impl FnArgExt for syn::FnArg {
125    fn typed(&self) -> Option<(&Ident, &syn::Type)> {
126        match self {
127            syn::FnArg::Typed(arg) => match *arg.pat {
128                syn::Pat::Ident(ref pat) => Some((&pat.ident, &arg.ty)),
129                _ => None
130            }
131            _ => None,
132        }
133    }
134
135    fn wild(&self) -> Option<&syn::PatWild> {
136        match self {
137            syn::FnArg::Typed(arg) => match *arg.pat {
138                syn::Pat::Wild(ref pat) => Some(pat),
139                _ => None
140            }
141            _ => None,
142        }
143    }
144}
145
146fn macro_inner_ty(t: &syn::TypeMacro, names: &[&str], m: MacTyMapFn) -> Option<syn::Type> {
147    if !names.iter().any(|k| t.mac.path.last_ident().map_or(false, |i| i == k)) {
148        return None;
149    }
150
151    let mut ty = m(&t.mac.tokens)?;
152    ty.strip_lifetimes();
153    Some(ty)
154}
155
156impl TypeExt for syn::Type {
157    fn unfold(&self) -> Vec<Child<'_>> {
158        self.unfold_with_ty_macros(&[], |_| None)
159    }
160
161    fn unfold_with_ty_macros(&self, names: &[&str], mapper: MacTyMapFn) -> Vec<Child<'_>> {
162        struct Visitor<'a, 'm> {
163            parents: Vec<Cow<'a, syn::Type>>,
164            children: Vec<Child<'a>>,
165            names: &'m [&'m str],
166            mapper: MacTyMapFn,
167        }
168
169        impl<'m> Visitor<'_, 'm> {
170            fn new(names: &'m [&'m str], mapper: MacTyMapFn) -> Self {
171                Visitor { parents: vec![], children: vec![], names, mapper }
172            }
173        }
174
175        impl<'a> Visit<'a> for Visitor<'a, '_> {
176            fn visit_type(&mut self, ty: &'a syn::Type) {
177                let parent = self.parents.last().cloned();
178
179                if let syn::Type::Macro(t) = ty {
180                    if let Some(inner_ty) = macro_inner_ty(t, self.names, self.mapper) {
181                        let mut visitor = Visitor::new(self.names, self.mapper);
182                        if let Some(parent) = parent.clone().into_owned() {
183                            visitor.parents.push(parent);
184                        }
185
186                        visitor.visit_type(&inner_ty);
187                        let mut children = visitor.children.into_owned();
188                        self.children.append(&mut children);
189                        return;
190                    }
191                }
192
193                self.children.push(Child { parent, ty: Cow::Borrowed(ty) });
194                self.parents.push(Cow::Borrowed(ty));
195                syn::visit::visit_type(self, ty);
196                self.parents.pop();
197            }
198        }
199
200        let mut visitor = Visitor::new(names, mapper);
201        visitor.visit_type(self);
202        visitor.children
203    }
204
205    fn is_concrete(&self, generics: &[&Ident]) -> bool {
206        struct ConcreteVisitor<'i>(bool, &'i [&'i Ident]);
207
208        impl<'a, 'i> Visit<'a> for ConcreteVisitor<'i> {
209            fn visit_type(&mut self, ty: &'a syn::Type) {
210                use syn::Type::*;
211
212                match ty {
213                    Path(t) if self.1.iter().any(|i| t.path.is_ident(*i)) => {
214                        self.0 = false;
215                    }
216                    ImplTrait(_) | Infer(_) | Macro(_) => {
217                        self.0 = false;
218                    }
219                    BareFn(_) | Never(_) => {
220                        self.0 = true;
221                    },
222                    _ => syn::visit::visit_type(self, ty),
223                }
224            }
225        }
226
227        let mut visitor = ConcreteVisitor(true, generics);
228        visitor.visit_type(self);
229        visitor.0
230    }
231}
232
233impl GenericsExt for syn::Generics {
234    fn type_idents(&self) -> Vec<&Ident> {
235        self.type_params().map(|p| &p.ident).collect()
236    }
237}
238
239#[cfg(test)]
240mod tests {
241    #[test]
242    fn test_type_unfold_is_generic() {
243        use super::{TypeExt, syn};
244
245        let ty: syn::Type = syn::parse_quote!(A<B, C<impl Foo>, Box<dyn Foo>, Option<T>>);
246        let children = ty.unfold();
247        assert_eq!(children.len(), 8);
248
249        let gen_ident = format_ident!("T");
250        let gen = &[&gen_ident];
251        assert_eq!(children.iter().filter(|c| c.ty.is_concrete(gen)).count(), 3);
252    }
253}