1use std::collections::HashSet;
12use syn::ext::IdentExt as _;
13use syn::visit::{
14 visit_bound_lifetimes, visit_generic_param, visit_lifetime, visit_type, visit_type_path,
15 visit_where_clause, Visit,
16};
17use syn::{GenericParam, Ident, Lifetime, Type, TypePath, WhereClause};
18
19use super::lifetimes::ignored_lifetime_ident;
20
21struct Visitor<'a> {
22 lt_param: &'a Ident,
24 found_lt_param_usage: bool,
26 typarams: &'a HashSet<Ident>,
28 found_typaram_usage: bool,
30 min_underscores_for_yoke_lt: usize,
37}
38
39impl<'ast> Visit<'ast> for Visitor<'_> {
40 fn visit_lifetime(&mut self, lt: &'ast Lifetime) {
41 if lt.ident.unraw() == *self.lt_param {
42 self.found_lt_param_usage = true;
45 }
46 visit_lifetime(self, lt)
47 }
48
49 fn visit_type_path(&mut self, ty: &'ast TypePath) {
50 if let Some(ident) = ty.path.get_ident() {
54 if self.typarams.contains(&ident.unraw()) {
55 self.found_typaram_usage = true;
56 }
57 }
58 visit_type_path(self, ty)
59 }
60
61 fn visit_bound_lifetimes(&mut self, lts: &'ast syn::BoundLifetimes) {
62 for lt in <s.lifetimes {
63 if let GenericParam::Lifetime(lt) = lt {
64 let maybe_underscores_yoke = lt.lifetime.ident.unraw().to_string();
65
66 if let Some(underscores) = maybe_underscores_yoke.strip_suffix("yoke") {
68 if underscores.as_bytes().iter().all(|byte| *byte == b'_') {
69 self.min_underscores_for_yoke_lt = self.min_underscores_for_yoke_lt.max(
73 underscores.len() + 1,
75 );
76 }
77 }
78 }
79 }
80 visit_bound_lifetimes(self, lts);
81 }
82 }
84
85#[derive(#[automatically_derived]
impl ::core::fmt::Debug for CheckResult {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f, "CheckResult",
"uses_lifetime_param", &self.uses_lifetime_param,
"uses_type_params", &self.uses_type_params,
"min_underscores_for_yoke_lt", &&self.min_underscores_for_yoke_lt)
}
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for CheckResult {
#[inline]
fn eq(&self, other: &CheckResult) -> bool {
self.uses_lifetime_param == other.uses_lifetime_param &&
self.uses_type_params == other.uses_type_params &&
self.min_underscores_for_yoke_lt ==
other.min_underscores_for_yoke_lt
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for CheckResult {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<bool>;
let _: ::core::cmp::AssertParamIsEq<usize>;
}
}Eq)]
86pub struct CheckResult {
87 pub uses_lifetime_param: bool,
90 pub uses_type_params: bool,
93 pub min_underscores_for_yoke_lt: usize,
100}
101
102pub fn check_type_for_parameters(
109 lt_param: &Ident,
110 typarams: &HashSet<Ident>,
111 ty: &Type,
112) -> CheckResult {
113 let mut visit = Visitor {
114 lt_param,
115 found_lt_param_usage: false,
116 typarams,
117 found_typaram_usage: false,
118 min_underscores_for_yoke_lt: 0,
119 };
120 visit_type(&mut visit, ty);
121
122 CheckResult {
123 uses_lifetime_param: visit.found_lt_param_usage,
124 uses_type_params: visit.found_typaram_usage,
125 min_underscores_for_yoke_lt: visit.min_underscores_for_yoke_lt,
126 }
127}
128
129pub fn check_parameter_for_bound_lts(param: &GenericParam) -> usize {
134 let mut visit = Visitor {
136 lt_param: &ignored_lifetime_ident(),
137 found_lt_param_usage: false,
138 typarams: &HashSet::new(),
139 found_typaram_usage: false,
140 min_underscores_for_yoke_lt: 0,
141 };
142
143 visit_generic_param(&mut visit, param);
144
145 visit.min_underscores_for_yoke_lt
146}
147
148pub fn check_where_clause_for_bound_lts(where_clause: &WhereClause) -> usize {
152 let mut visit = Visitor {
154 lt_param: &ignored_lifetime_ident(),
155 found_lt_param_usage: false,
156 typarams: &HashSet::new(),
157 found_typaram_usage: false,
158 min_underscores_for_yoke_lt: 0,
159 };
160
161 visit_where_clause(&mut visit, where_clause);
162
163 visit.min_underscores_for_yoke_lt
164}
165
166#[cfg(test)]
167mod tests {
168 use proc_macro2::Span;
169 use std::collections::HashSet;
170 use syn::{parse_quote, Ident};
171
172 use super::{check_type_for_parameters, CheckResult};
173
174 fn a_ident() -> Ident {
175 Ident::new("a", Span::call_site())
176 }
177
178 fn yoke_ident() -> Ident {
179 Ident::new("yoke", Span::call_site())
180 }
181
182 fn make_typarams(params: &[&str]) -> HashSet<Ident> {
183 params
184 .iter()
185 .map(|x| Ident::new(x, Span::call_site()))
186 .collect()
187 }
188
189 fn uses(lifetime: bool, typarams: bool) -> CheckResult {
190 CheckResult {
191 uses_lifetime_param: lifetime,
192 uses_type_params: typarams,
193 min_underscores_for_yoke_lt: 0,
194 }
195 }
196
197 #[test]
198 fn test_simple_type() {
199 let environment = make_typarams(&["T", "U", "V"]);
200
201 let ty = parse_quote!(Foo<'a, T>);
202 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
203 assert_eq!(check, uses(true, true));
204
205 let ty = parse_quote!(Foo<T>);
206 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
207 assert_eq!(check, uses(false, true));
208
209 let ty = parse_quote!(Foo<'static, T>);
210 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
211 assert_eq!(check, uses(false, true));
212
213 let ty = parse_quote!(Foo<'a>);
214 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
215 assert_eq!(check, uses(true, false));
216
217 let ty = parse_quote!(Foo<'a, Bar<U>, Baz<(V, u8)>>);
218 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
219 assert_eq!(check, uses(true, true));
220
221 let ty = parse_quote!(Foo<'a, W>);
222 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
223 assert_eq!(check, uses(true, false));
224 }
225
226 #[test]
227 fn test_assoc_types() {
228 let environment = make_typarams(&["T"]);
229
230 let ty = parse_quote!(<Foo as SomeTrait<'a, T>>::Output);
231 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
232 assert_eq!(check, uses(true, true));
233
234 let ty = parse_quote!(<Foo as SomeTrait<'static, T>>::Output);
235 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
236 assert_eq!(check, uses(false, true));
237
238 let ty = parse_quote!(<T as SomeTrait<'static, Foo>>::Output);
239 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
240 assert_eq!(check, uses(false, true));
241 }
242
243 #[test]
244 fn test_macro_types() {
245 let environment = make_typarams(&["T"]);
246
247 let ty = parse_quote!(foo!(Foo<'a, T>));
251 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
252 assert_eq!(check, uses(false, false));
253
254 let ty = parse_quote!(foo!(Foo<T>));
255 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
256 assert_eq!(check, uses(false, false));
257
258 let ty = parse_quote!(foo!(Foo<'static, T>));
259 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
260 assert_eq!(check, uses(false, false));
261
262 let ty = parse_quote!(foo!(Foo<'a>));
263 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
264 assert_eq!(check, uses(false, false));
265
266 let ty = parse_quote!(foo!(Foo<'a, Bar<U>, Baz<(V, u8)>>));
267 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
268 assert_eq!(check, uses(false, false));
269
270 let ty = parse_quote!(foo!(Foo<'a, W>));
271 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
272 assert_eq!(check, uses(false, false));
273 }
274
275 #[test]
276 fn test_raw_types() {
277 let environment = make_typarams(&["T", "U", "V"]);
278
279 let ty = parse_quote!(Foo<'a, r#T>);
280 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
281 assert_eq!(check, uses(true, true));
282
283 let ty = parse_quote!(Foo<r#T>);
284 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
285 assert_eq!(check, uses(false, true));
286
287 let ty = parse_quote!(Foo<'static, r#T>);
288 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
289 assert_eq!(check, uses(false, true));
290
291 let ty = parse_quote!(Foo<'a>);
292 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
293 assert_eq!(check, uses(true, false));
294
295 let ty = parse_quote!(Foo<'a, Bar<r#U>, Baz<(r#V, u8)>>);
296 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
297 assert_eq!(check, uses(true, true));
298
299 let ty = parse_quote!(Foo<'a, r#W>);
300 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
301 assert_eq!(check, uses(true, false));
302 }
303
304 #[test]
305 fn test_yoke_lifetime() {
306 let environment = make_typarams(&["T", "U", "V"]);
307
308 let ty = parse_quote!(Foo<'yoke, r#T>);
309 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
310 assert_eq!(check.min_underscores_for_yoke_lt, 0);
311
312 let ty = parse_quote!(for<'yoke> fn(&'yoke ()));
313 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
314 assert_eq!(check.min_underscores_for_yoke_lt, 1);
315
316 let ty = parse_quote!(for<'_yoke> fn(&'_yoke ()));
317 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
318 assert_eq!(check.min_underscores_for_yoke_lt, 2);
319
320 let ty = parse_quote!(for<'_yoke_> fn(&'_yoke_ ()));
321 let check = check_type_for_parameters(&yoke_ident(), &environment, &ty);
322 assert_eq!(check.min_underscores_for_yoke_lt, 0);
323
324 let ty = parse_quote!(for<'_yoke, '___yoke> fn(&'_yoke (), &'___yoke ()));
325 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
326 assert_eq!(check.min_underscores_for_yoke_lt, 4);
327
328 let ty = parse_quote!(for<'___yoke> fn(for<'_yoke> fn(&'_yoke (), &'___yoke ())));
329 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
330 assert_eq!(check.min_underscores_for_yoke_lt, 4);
331
332 let ty = parse_quote! {
333 for<'yoke> fn(for<'_yoke> fn(for<'b> fn(&'b (), &'_yoke (), &'yoke ())))
334 };
335 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
336 assert_eq!(check.min_underscores_for_yoke_lt, 2);
337
338 let ty = parse_quote! {
339 for<'yoke> fn(for<'r#_yoke> fn(for<'b> fn(&'b (), &'_yoke (), &'yoke ())))
340 };
341 let check = check_type_for_parameters(&a_ident(), &environment, &ty);
342 assert_eq!(check.min_underscores_for_yoke_lt, 2);
343 }
344}