rocket_codegen/
syn_ext.rs1use 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 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}