1use std::borrow::Cow;
2use std::collections::{BTreeMap, BTreeSet};
3use syn::parse_quote;
4use syn::visit_mut::{self, VisitMut};
5use syn::{visit::Visit, Ident, Type};
6
7use crate::error::Diagnostic;
8
9struct GenericRenameVisitor<'a> {
12 renames: &'a BTreeMap<&'a Ident, Option<Cow<'a, syn::Type>>>,
13 err: Option<Diagnostic>,
14}
15
16impl<'a> VisitMut for GenericRenameVisitor<'a> {
17 fn visit_type_mut(&mut self, ty: &mut Type) {
18 if self.err.is_some() {
19 return;
20 }
21 if let Type::Path(type_path) = ty {
22 if let Some(qself) = &mut type_path.qself {
24 if let Type::Path(qself_path) = &mut *qself.ty {
25 if qself_path.qself.is_none() && qself_path.path.segments.len() == 1 {
26 let ident = &qself_path.path.segments[0].ident;
27 if let Some((_, concrete)) = self.renames.get_key_value(ident) {
28 *qself.ty = if let Some(concrete) = concrete {
29 concrete.clone().into_owned()
30 } else {
31 parse_quote! { JsValue }
32 };
33 return;
34 }
35 }
36 }
37 }
38 if type_path.qself.is_none() && !type_path.path.segments.is_empty() {
40 let first_seg = &type_path.path.segments[0];
41
42 if let Some((_, concrete)) = self.renames.get_key_value(&first_seg.ident) {
43 if let Some(concrete) = concrete {
44 if type_path.path.segments.len() == 1 {
45 *ty = concrete.clone().into_owned();
46 } else if let Type::Path(concrete_path) = concrete.as_ref() {
47 let remaining: Vec<_> =
48 type_path.path.segments.iter().skip(1).cloned().collect();
49 type_path.path.segments = concrete_path.path.segments.clone();
50 type_path.path.segments.extend(remaining);
51 }
52 } else {
53 *ty = parse_quote! { JsValue };
54 }
55 return;
56 }
57 }
58 }
59 visit_mut::visit_type_mut(self, ty);
60 }
61}
62
63#[derive(Debug)]
65pub struct GenericNameVisitor<'a, 'b> {
66 generic_params: &'a Vec<&'a Ident>,
67 found_set: &'b mut BTreeSet<Ident>,
69}
70
71impl<'a, 'b> GenericNameVisitor<'a, 'b> {
73 pub fn new(generic_params: &'a Vec<&'a Ident>, found_set: &'b mut BTreeSet<Ident>) -> Self {
76 Self {
77 generic_params,
78 found_set,
79 }
80 }
81}
82
83impl<'a, 'b> Visit<'a> for GenericNameVisitor<'a, 'b> {
84 fn visit_type_reference(&mut self, type_ref: &'a syn::TypeReference) {
85 if let syn::Type::Path(type_path) = &*type_ref.elem {
86 if let Some(qself) = &type_path.qself {
88 syn::visit::visit_type(self, &qself.ty);
89 for segment in &type_path.path.segments {
91 syn::visit::visit_path_segment(self, segment);
92 }
93 return;
94 }
95
96 if let Some(first_segment) = type_path.path.segments.first() {
97 if type_path.path.segments.len() == 1 && first_segment.arguments.is_empty() {
98 if self.generic_params.contains(&&first_segment.ident) {
99 self.found_set.insert(first_segment.ident.clone());
100 return;
101 }
102 } else {
103 if self.generic_params.contains(&&first_segment.ident) {
104 self.found_set.insert(first_segment.ident.clone());
105 }
106
107 syn::visit::visit_path_arguments(self, &first_segment.arguments);
108
109 for segment in type_path.path.segments.iter().skip(1) {
110 syn::visit::visit_path_segment(self, segment);
111 }
112 return;
113 }
114 }
115 }
116
117 syn::visit::visit_type_reference(self, type_ref);
119 }
120
121 fn visit_path(&mut self, path: &'a syn::Path) {
122 if let Some(first_segment) = path.segments.first() {
123 if self.generic_params.contains(&&first_segment.ident) {
124 self.found_set.insert(first_segment.ident.clone());
125 }
126 }
127
128 for segment in &path.segments {
129 match &segment.arguments {
130 syn::PathArguments::AngleBracketed(args) => {
131 for arg in &args.args {
132 match arg {
133 syn::GenericArgument::Type(ty) => {
134 syn::visit::visit_type(self, ty);
135 }
136 syn::GenericArgument::AssocType(binding) => {
137 syn::visit::visit_type(self, &binding.ty);
139 }
140 _ => {
141 syn::visit::visit_generic_argument(self, arg);
142 }
143 }
144 }
145 }
146 syn::PathArguments::Parenthesized(args) => {
147 for input in &args.inputs {
149 syn::visit::visit_type(self, input);
150 }
151 if let syn::ReturnType::Type(_, return_type) = &args.output {
152 syn::visit::visit_type(self, return_type);
153 }
154 }
155 syn::PathArguments::None => {}
156 }
157 }
158 }
159}
160
161pub(crate) fn generic_params(generics: &syn::Generics) -> Vec<(&Ident, Option<&syn::Type>)> {
163 generics
164 .type_params()
165 .map(|tp| (&tp.ident, tp.default.as_ref()))
166 .collect()
167}
168
169pub(crate) fn type_params_with_bounds(generics: &syn::Generics) -> Vec<proc_macro2::TokenStream> {
173 generics
174 .type_params()
175 .map(|tp| {
176 let ident = &tp.ident;
177 let bounds = &tp.bounds;
178 if bounds.is_empty() {
179 quote::quote! { #ident }
180 } else {
181 quote::quote! { #ident: #bounds }
182 }
183 })
184 .collect()
185}
186pub(crate) fn generic_bounds<'a>(generics: &'a syn::Generics) -> Vec<Cow<'a, syn::WherePredicate>> {
188 let mut bounds = Vec::new();
189 for param in &generics.params {
190 if let syn::GenericParam::Type(type_param) = param {
191 if !type_param.bounds.is_empty() {
192 let ident = &type_param.ident;
193 let predicate = syn::WherePredicate::Type(syn::PredicateType {
194 lifetimes: None,
195 bounded_ty: syn::parse_quote!(#ident),
196 colon_token: syn::Token),
197 bounds: type_param.bounds.clone(),
198 });
199 bounds.push(Cow::Owned(predicate));
200 }
201 }
202 }
203 if let Some(where_clause) = &generics.where_clause {
204 bounds.extend(where_clause.predicates.iter().map(Cow::Borrowed));
205 }
206 bounds
207}
208
209pub(crate) fn staticize_lifetimes(
214 mut ty: syn::Type,
215 lifetimes_to_staticize: &[&syn::Lifetime],
216) -> syn::Type {
217 struct LifetimeStaticizer<'a> {
218 lifetimes: &'a [&'a syn::Lifetime],
219 }
220 impl VisitMut for LifetimeStaticizer<'_> {
221 fn visit_lifetime_mut(&mut self, lifetime: &mut syn::Lifetime) {
222 if self.lifetimes.iter().any(|lt| lt.ident == lifetime.ident) {
223 *lifetime = syn::Lifetime::new("'static", lifetime.span());
224 }
225 }
226 }
227 LifetimeStaticizer {
228 lifetimes: lifetimes_to_staticize,
229 }
230 .visit_type_mut(&mut ty);
231 ty
232}
233
234pub(crate) fn generic_param_names(generics: &syn::Generics) -> Vec<&Ident> {
236 generics.type_params().map(|tp| &tp.ident).collect()
237}
238
239pub(crate) fn lifetime_params(generics: &syn::Generics) -> Vec<&syn::Lifetime> {
241 generics.lifetimes().map(|lp| &lp.lifetime).collect()
242}
243
244pub(crate) fn all_param_names(generics: &syn::Generics) -> (Vec<&syn::Lifetime>, Vec<&Ident>) {
246 (lifetime_params(generics), generic_param_names(generics))
247}
248
249pub struct LifetimeVisitor<'a> {
251 lifetime_params: &'a [&'a syn::Lifetime],
252 found_set: BTreeSet<syn::Lifetime>,
253}
254
255impl<'a> LifetimeVisitor<'a> {
256 pub fn new(lifetime_params: &'a [&'a syn::Lifetime]) -> Self {
257 Self {
258 lifetime_params,
259 found_set: BTreeSet::new(),
260 }
261 }
262
263 pub fn into_found(self) -> BTreeSet<syn::Lifetime> {
264 self.found_set
265 }
266}
267
268impl<'ast> syn::visit::Visit<'ast> for LifetimeVisitor<'_> {
269 fn visit_lifetime(&mut self, lifetime: &'ast syn::Lifetime) {
270 if self.lifetime_params.contains(&lifetime) {
271 self.found_set.insert(lifetime.clone());
272 }
273 }
274}
275
276pub(crate) fn used_lifetimes_in_type<'a>(
278 ty: &syn::Type,
279 lifetime_params: &'a [&'a syn::Lifetime],
280) -> BTreeSet<syn::Lifetime> {
281 let mut visitor = LifetimeVisitor::new(lifetime_params);
282 syn::visit::Visit::visit_type(&mut visitor, ty);
283 visitor.into_found()
284}
285
286pub(crate) fn uses_generic_params(ty: &syn::Type, generic_names: &Vec<&Ident>) -> bool {
287 let mut found_set = Default::default();
288 let mut visitor = GenericNameVisitor::new(generic_names, &mut found_set);
289 visitor.visit_type(ty);
290 !found_set.is_empty()
291}
292
293pub(crate) fn uses_lifetime_params(ty: &syn::Type, lifetime_params: &[&syn::Lifetime]) -> bool {
294 !used_lifetimes_in_type(ty, lifetime_params).is_empty()
295}
296
297pub(crate) fn used_lifetimes_in_bounds<'a>(
299 bounds: &syn::punctuated::Punctuated<syn::TypeParamBound, syn::token::Plus>,
300 lifetime_params: &'a [&'a syn::Lifetime],
301) -> BTreeSet<syn::Lifetime> {
302 let mut visitor = LifetimeVisitor::new(lifetime_params);
303 for bound in bounds {
304 syn::visit::Visit::visit_type_param_bound(&mut visitor, bound);
305 }
306 visitor.into_found()
307}
308
309pub(crate) fn used_generic_params<'a>(
310 ty: &'a syn::Type,
311 generic_names: &'a Vec<&Ident>,
312 mut used_params: BTreeSet<Ident>,
313) -> BTreeSet<Ident> {
314 let mut visitor = GenericNameVisitor::new(generic_names, &mut used_params);
315 visitor.visit_type(ty);
316 used_params
317}
318
319pub(crate) fn generics_predicate_uses(
321 predicate: &syn::WherePredicate,
322 generic_names: &Vec<&Ident>,
323) -> bool {
324 let mut found_set = Default::default();
325 let mut visitor = GenericNameVisitor::new(generic_names, &mut found_set);
326 visitor.visit_where_predicate(predicate);
327 !found_set.is_empty()
328}
329
330pub(crate) fn generic_to_concrete<'a>(
335 mut ty: syn::Type,
336 generic_names: &BTreeMap<&'a Ident, Option<Cow<'a, syn::Type>>>,
337 lifetimes_to_staticize: &[&syn::Lifetime],
338) -> Result<syn::Type, Diagnostic> {
339 if !generic_names.is_empty() {
341 let mut visitor = GenericRenameVisitor {
342 renames: generic_names,
343 err: None,
344 };
345 visitor.visit_type_mut(&mut ty);
346 if let Some(err) = visitor.err {
347 return Err(err);
348 }
349 }
350 Ok(staticize_lifetimes(ty, lifetimes_to_staticize))
352}
353
354#[cfg(test)]
355mod tests {
356 #[test]
357 fn test_generic_name_visitor() {
358 let t_ident = syn::Ident::new("T", proc_macro2::Span::call_site());
359 let u_ident = syn::Ident::new("U", proc_macro2::Span::call_site());
360 let generic_params = vec![&t_ident, &u_ident];
361
362 let ty: syn::Type = syn::parse_quote!(T);
364 let mut found_set = Default::default();
365 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
366 syn::visit::visit_type(&mut visitor, &ty);
367 assert!(visitor.found_set.contains(&t_ident));
368
369 let ty: syn::Type = syn::parse_quote!(&T);
371 let mut found_set = Default::default();
372 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
373 syn::visit::visit_type(&mut visitor, &ty);
374 assert!(visitor.found_set.contains(&t_ident));
375
376 let ty: syn::Type = syn::parse_quote!(T<U>);
378 let mut found_set = Default::default();
379 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
380 syn::visit::visit_type(&mut visitor, &ty);
381 assert!(visitor.found_set.contains(&t_ident));
382 assert!(visitor.found_set.contains(&u_ident));
383
384 let ty: syn::Type = syn::parse_quote!(&T<U>);
386 let mut found_set = Default::default();
387 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
388 syn::visit::visit_type(&mut visitor, &ty);
389 assert!(visitor.found_set.contains(&t_ident));
390 assert!(visitor.found_set.contains(&u_ident));
391
392 let ty: syn::Type = syn::parse_quote!(T::<U>::Foo);
394 let mut found_set = Default::default();
395 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
396 syn::visit::visit_type(&mut visitor, &ty);
397 assert!(visitor.found_set.contains(&t_ident));
398 assert!(visitor.found_set.contains(&u_ident));
399
400 let ty: syn::Type = syn::parse_quote!(Vec<T>);
402 let mut found_set = Default::default();
403 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
404 syn::visit::visit_type(&mut visitor, &ty);
405 assert!(visitor.found_set.contains(&t_ident));
406 assert!(!visitor.found_set.contains(&u_ident));
407 }
408
409 #[test]
410 fn test_associated_type_binding() {
411 let t_ident = syn::Ident::new("T", proc_macro2::Span::call_site());
412 let u_ident = syn::Ident::new("U", proc_macro2::Span::call_site());
413 let generic_params = vec![&t_ident, &u_ident];
414
415 let ty: syn::Type = syn::parse_quote!(SomeTrait<T = U>);
417 let mut found_set = Default::default();
418 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
419 syn::visit::visit_type(&mut visitor, &ty);
420 assert!(!visitor.found_set.contains(&t_ident)); assert!(visitor.found_set.contains(&u_ident)); let ty: syn::Type = syn::parse_quote!(SomeTrait<U = T>);
425 let mut found_set = Default::default();
426 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
427 syn::visit::visit_type(&mut visitor, &ty);
428 assert!(visitor.found_set.contains(&t_ident)); assert!(!visitor.found_set.contains(&u_ident)); }
431
432 #[test]
433 fn test_nested_references() {
434 let t_ident = syn::Ident::new("T", proc_macro2::Span::call_site());
435 let u_ident = syn::Ident::new("U", proc_macro2::Span::call_site());
436 let generic_params = vec![&t_ident, &u_ident];
437
438 let ty: syn::Type = syn::parse_quote!(&T);
440 let mut found_set = Default::default();
441 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
442 syn::visit::visit_type(&mut visitor, &ty);
443 assert!(visitor.found_set.contains(&t_ident));
444
445 let ty: syn::Type = syn::parse_quote!(&&T);
447 let mut found_set = Default::default();
448 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
449 syn::visit::visit_type(&mut visitor, &ty);
450 assert!(visitor.found_set.contains(&t_ident));
451
452 let ty: syn::Type = syn::parse_quote!(&&&T);
454 let mut found_set = Default::default();
455 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
456 syn::visit::visit_type(&mut visitor, &ty);
457 assert!(visitor.found_set.contains(&t_ident));
458
459 let ty: syn::Type = syn::parse_quote!(&T<U>);
461 let mut found_set = Default::default();
462 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
463 syn::visit::visit_type(&mut visitor, &ty);
464 assert!(visitor.found_set.contains(&t_ident));
465 assert!(visitor.found_set.contains(&u_ident));
466 }
467
468 #[test]
469 fn test_mixed_usage() {
470 let t_ident = syn::Ident::new("T", proc_macro2::Span::call_site());
471 let generic_params = vec![&t_ident];
472
473 let ty: syn::Type = syn::parse_quote!(SomeTrait<Item = T> + OtherTrait<Ref = &T>);
475 let mut found_set = Default::default();
476 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
477 syn::visit::visit_type(&mut visitor, &ty);
478 assert!(visitor.found_set.contains(&t_ident));
479 }
480
481 #[test]
482 fn test_ref_qself_trait_assoc_type() {
483 let t_ident = syn::Ident::new("T", proc_macro2::Span::call_site());
484 let generic_params = vec![&t_ident];
485
486 let ty: syn::Type = syn::parse_quote!(&<T as JsFunction1>::Arg1);
488 let mut found_set = Default::default();
489 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
490 syn::visit::visit_type(&mut visitor, &ty);
491 assert!(
492 visitor.found_set.contains(&t_ident),
493 "T should be found in &<T as JsFunction1>::Arg1"
494 );
495 }
496
497 #[test]
498 fn test_complex_reference_with_closure() {
499 let t_ident = syn::Ident::new("T", proc_macro2::Span::call_site());
500 let r_ident = syn::Ident::new("R", proc_macro2::Span::call_site());
501 let generic_params = vec![&t_ident, &r_ident];
502
503 let ty: syn::Type = syn::parse_quote!(&Closure<dyn FnMut(T) -> Result<R, JsValue>>);
504
505 let mut found_set = Default::default();
506 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
507 syn::visit::visit_type(&mut visitor, &ty);
508
509 assert!(visitor.found_set.contains(&t_ident));
510 assert!(visitor.found_set.contains(&r_ident));
511 }
512
513 #[test]
514 fn test_generic_args_to_concrete() {
515 use std::borrow::Cow;
516 use std::collections::BTreeMap;
517
518 let t = syn::parse_quote!(T);
520 let str: syn::Type = syn::parse_quote!(String);
521 let generic_names: BTreeMap<&syn::Ident, Option<Cow<syn::Type>>> = {
522 let mut map = BTreeMap::new();
523 map.insert(&t, Some(Cow::Borrowed(&str)));
524 map
525 };
526
527 let generic_type: syn::Type = syn::parse_quote!(Promise<T>);
529 let result =
530 crate::generics::generic_to_concrete(generic_type, &generic_names, &[]).unwrap();
531 let expected: syn::Type = syn::parse_quote!(Promise<String>);
532 assert_eq!(
533 quote::quote!(#result).to_string(),
534 quote::quote!(#expected).to_string()
535 );
536
537 let mixed_type: syn::Type = syn::parse_quote!(Promise<i32, T>);
539 let result = crate::generics::generic_to_concrete(mixed_type, &generic_names, &[]).unwrap();
540 let expected: syn::Type = syn::parse_quote!(Promise<i32, String>);
541 assert_eq!(
542 quote::quote!(#result).to_string(),
543 quote::quote!(#expected).to_string()
544 );
545
546 let concrete_type: syn::Type = syn::parse_quote!(Promise<i32, bool>);
548 let result =
549 crate::generics::generic_to_concrete(concrete_type, &generic_names, &[]).unwrap();
550 let expected: syn::Type = syn::parse_quote!(Promise<i32, bool>);
551 assert_eq!(
552 quote::quote!(#result).to_string(),
553 quote::quote!(#expected).to_string()
554 );
555 }
556
557 #[test]
558 fn test_generic_associated_type_replacement() {
559 use std::borrow::Cow;
560 use std::collections::BTreeMap;
561
562 let t: syn::Ident = syn::parse_quote!(T);
563 let concrete: syn::Type = syn::parse_quote!(MyConcreteType);
564 let generic_names: BTreeMap<&syn::Ident, Option<Cow<syn::Type>>> = {
565 let mut map = BTreeMap::new();
566 map.insert(&t, Some(Cow::Borrowed(&concrete)));
567 map
568 };
569
570 let assoc_type: syn::Type = syn::parse_quote!(T::DurableObjectStub);
572 let result = crate::generics::generic_to_concrete(assoc_type, &generic_names, &[]).unwrap();
573 let expected: syn::Type = syn::parse_quote!(MyConcreteType::DurableObjectStub);
574 assert_eq!(
575 quote::quote!(#result).to_string(),
576 quote::quote!(#expected).to_string()
577 );
578
579 let nested: syn::Type = syn::parse_quote!(Vec<T::Item>);
581 let result = crate::generics::generic_to_concrete(nested, &generic_names, &[]).unwrap();
582 let expected: syn::Type = syn::parse_quote!(Vec<MyConcreteType::Item>);
583 assert_eq!(
584 quote::quote!(#result).to_string(),
585 quote::quote!(#expected).to_string()
586 );
587
588 let complex: syn::Type = syn::parse_quote!(WasmRet<<T::Stub as FromWasmAbi>::Abi>);
590 let result = crate::generics::generic_to_concrete(complex, &generic_names, &[]).unwrap();
591 let expected: syn::Type =
592 syn::parse_quote!(WasmRet<<MyConcreteType::Stub as FromWasmAbi>::Abi>);
593 assert_eq!(
594 quote::quote!(#result).to_string(),
595 quote::quote!(#expected).to_string()
596 );
597
598 let with_args: syn::Type = syn::parse_quote!(T<SomeArg>);
600 let result = crate::generics::generic_to_concrete(with_args, &generic_names, &[]).unwrap();
601 let expected: syn::Type = syn::parse_quote!(MyConcreteType);
602 assert_eq!(
603 quote::quote!(#result).to_string(),
604 quote::quote!(#expected).to_string()
605 );
606
607 let qself_type: syn::Type = syn::parse_quote!(<T::DurableObjectStub as FromWasmAbi>::Abi);
609 let result = crate::generics::generic_to_concrete(qself_type, &generic_names, &[]).unwrap();
610 let expected: syn::Type =
611 syn::parse_quote!(<MyConcreteType::DurableObjectStub as FromWasmAbi>::Abi);
612 assert_eq!(
613 quote::quote!(#result).to_string(),
614 quote::quote!(#expected).to_string()
615 );
616
617 let qself_trait: syn::Type = syn::parse_quote!(<T as DurableObject>::DurableObjectStub);
619 let result =
620 crate::generics::generic_to_concrete(qself_trait, &generic_names, &[]).unwrap();
621 let expected: syn::Type =
622 syn::parse_quote!(<MyConcreteType as DurableObject>::DurableObjectStub);
623 assert_eq!(
624 quote::quote!(#result).to_string(),
625 quote::quote!(#expected).to_string()
626 );
627
628 let ref_qself_trait: syn::Type =
630 syn::parse_quote!(&<T as DurableObject>::DurableObjectStub);
631 let result =
632 crate::generics::generic_to_concrete(ref_qself_trait, &generic_names, &[]).unwrap();
633 let expected: syn::Type =
634 syn::parse_quote!(&<MyConcreteType as DurableObject>::DurableObjectStub);
635 assert_eq!(
636 quote::quote!(#result).to_string(),
637 quote::quote!(#expected).to_string()
638 );
639 }
640
641 #[test]
642 fn test_where_predicate_assoc_type_binding() {
643 let f_ident = syn::Ident::new("F", proc_macro2::Span::call_site());
648 let ret_ident = syn::Ident::new("Ret", proc_macro2::Span::call_site());
649
650 let generic_params = vec![&f_ident, &ret_ident];
652 let predicate: syn::WherePredicate = syn::parse_quote!(F: JsFunction<Ret = Ret>);
653
654 let mut found_set = Default::default();
655 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
656 syn::visit::visit_where_predicate(&mut visitor, &predicate);
657
658 assert!(
659 found_set.contains(&f_ident),
660 "F should be found in 'F: JsFunction<Ret = Ret>'"
661 );
662 assert!(
663 found_set.contains(&ret_ident),
664 "Ret should be found in 'F: JsFunction<Ret = Ret>' (RHS of assoc type binding)"
665 );
666 }
667
668 #[test]
669 fn test_where_predicate_assoc_type_binding_only_rhs() {
670 let f_ident = syn::Ident::new("F", proc_macro2::Span::call_site());
671 let ret_ident = syn::Ident::new("Ret", proc_macro2::Span::call_site());
672
673 let generic_params = vec![&ret_ident];
675 let predicate: syn::WherePredicate = syn::parse_quote!(F: JsFunction<Ret = Ret>);
676
677 let uses = crate::generics::generics_predicate_uses(&predicate, &generic_params);
678 assert!(
679 uses,
680 "Ret should be detected as used in 'F: JsFunction<Ret = Ret>'"
681 );
682
683 let not_generic_params = vec![&f_ident];
685 let uses = crate::generics::generics_predicate_uses(&predicate, ¬_generic_params);
686 assert!(
687 uses,
688 "F should not be detected as used in 'F: JsFunction<Ret = Ret>'"
689 );
690 }
691
692 #[test]
693 fn test_where_predicate_assoc_type_binding_only_bounded() {
694 let f_ident = syn::Ident::new("F", proc_macro2::Span::call_site());
696 let ret_ident = syn::Ident::new("Ret", proc_macro2::Span::call_site());
697
698 let generic_params = vec![&f_ident];
700 let predicate: syn::WherePredicate = syn::parse_quote!(F: JsFunction<Ret = Ret>);
701
702 let uses = crate::generics::generics_predicate_uses(&predicate, &generic_params);
703 assert!(
704 uses,
705 "F should be detected as used in 'F: JsFunction<Ret = Ret>'"
706 );
707
708 let mut found_set = Default::default();
710 let mut visitor = crate::generics::GenericNameVisitor::new(&generic_params, &mut found_set);
711 syn::visit::visit_where_predicate(&mut visitor, &predicate);
712
713 assert!(found_set.contains(&f_ident), "F should be found");
714 assert!(
715 !found_set.contains(&ret_ident),
716 "Ret should NOT be found when not in search set"
717 );
718 }
719
720 #[test]
721 fn test_staticize_specific_lifetimes() {
722 let lifetime_a: syn::Lifetime = syn::parse_quote!('a);
724 let lifetimes = [&lifetime_a];
725
726 let ty: syn::Type = syn::parse_quote!(ImmediateClosure<'a, dyn FnMut(T) -> R>);
727 let result = crate::generics::staticize_lifetimes(ty, &lifetimes);
728 let expected: syn::Type = syn::parse_quote!(ImmediateClosure<'static, dyn FnMut(T) -> R>);
729 assert_eq!(
730 quote::quote!(#result).to_string(),
731 quote::quote!(#expected).to_string()
732 );
733
734 let lifetime_b: syn::Lifetime = syn::parse_quote!('b);
736 let lifetimes_both = [&lifetime_a, &lifetime_b];
737 let ty: syn::Type = syn::parse_quote!(&'a SomeType<'b, T>);
738 let result = crate::generics::staticize_lifetimes(ty, &lifetimes_both);
739 let expected: syn::Type = syn::parse_quote!(&'static SomeType<'static, T>);
740 assert_eq!(
741 quote::quote!(#result).to_string(),
742 quote::quote!(#expected).to_string()
743 );
744
745 let ty: syn::Type = syn::parse_quote!(&'a SomeType<'b, T>);
747 let result = crate::generics::staticize_lifetimes(ty, &[&lifetime_a]);
748 let expected: syn::Type = syn::parse_quote!(&'static SomeType<'b, T>);
749 assert_eq!(
750 quote::quote!(#result).to_string(),
751 quote::quote!(#expected).to_string()
752 );
753
754 let ty: syn::Type = syn::parse_quote!(Vec<T>);
756 let result = crate::generics::staticize_lifetimes(ty, &[]);
757 let expected: syn::Type = syn::parse_quote!(Vec<T>);
758 assert_eq!(
759 quote::quote!(#result).to_string(),
760 quote::quote!(#expected).to_string()
761 );
762 }
763
764 #[test]
765 fn test_generic_to_concrete_with_lifetimes() {
766 use std::borrow::Cow;
767 use std::collections::BTreeMap;
768
769 let t: syn::Ident = syn::parse_quote!(T);
771 let concrete: syn::Type = syn::parse_quote!(JsValue);
772 let generic_names: BTreeMap<&syn::Ident, Option<Cow<syn::Type>>> = {
773 let mut map = BTreeMap::new();
774 map.insert(&t, Some(Cow::Borrowed(&concrete)));
775 map
776 };
777
778 let lifetime_a: syn::Lifetime = syn::parse_quote!('a);
780 let lifetimes_to_staticize = [&lifetime_a];
781
782 let ty: syn::Type = syn::parse_quote!(ImmediateClosure<'a, dyn FnMut(T)>);
784 let result =
785 crate::generics::generic_to_concrete(ty, &generic_names, &lifetimes_to_staticize)
786 .unwrap();
787 let expected: syn::Type = syn::parse_quote!(ImmediateClosure<'static, dyn FnMut(JsValue)>);
788 assert_eq!(
789 quote::quote!(#result).to_string(),
790 quote::quote!(#expected).to_string()
791 );
792
793 let _lifetime_b: syn::Lifetime = syn::parse_quote!('b);
795 let lifetimes_only_a = [&lifetime_a];
796 let ty: syn::Type = syn::parse_quote!(Foo<'a, 'b>);
797 let result =
798 crate::generics::generic_to_concrete(ty, &BTreeMap::new(), &lifetimes_only_a).unwrap();
799 let expected: syn::Type = syn::parse_quote!(Foo<'static, 'b>);
800 assert_eq!(
801 quote::quote!(#result).to_string(),
802 quote::quote!(#expected).to_string()
803 );
804 }
805}