|
| 1 | +use std::rc::Rc; |
| 2 | +use syn::parse_quote; |
| 3 | +use syn::visit::{self, Visit}; |
| 4 | +use syn::{Ident, Lifetime}; |
| 5 | + |
| 6 | +pub(crate) fn extract_referenced_generics( |
| 7 | + ty: &syn::Type, |
| 8 | + generics: &syn::Generics, |
| 9 | + errors: &mut Vec<Rc<syn::Error>>, |
| 10 | +) -> syn::Generics { |
| 11 | + struct Visitor<'g, 'errs> { |
| 12 | + lifetimes: Vec<(&'g Lifetime, bool)>, |
| 13 | + type_parameters: Vec<(&'g Ident, bool)>, |
| 14 | + errors: &'errs mut Vec<Rc<syn::Error>>, |
| 15 | + } |
| 16 | + |
| 17 | + let mut visitor = Visitor { |
| 18 | + lifetimes: generics |
| 19 | + .lifetimes() |
| 20 | + .map(|lt| (<.lifetime, false)) |
| 21 | + .collect(), |
| 22 | + type_parameters: generics |
| 23 | + .type_params() |
| 24 | + .map(|tp| (&tp.ident, false)) |
| 25 | + .collect(), |
| 26 | + errors, |
| 27 | + }; |
| 28 | + visitor.lifetimes.sort_unstable(); |
| 29 | + visitor.type_parameters.sort_unstable(); |
| 30 | + |
| 31 | + impl<'ast> Visit<'ast> for Visitor<'_, '_> { |
| 32 | + fn visit_lifetime(&mut self, lifetime: &'ast Lifetime) { |
| 33 | + if lifetime.ident == "_" { |
| 34 | + self.errors.push(Rc::new(syn::Error::new_spanned( |
| 35 | + lifetime, |
| 36 | + "`#[auto_type]` requires named lifetimes", |
| 37 | + ))); |
| 38 | + } else if lifetime.ident != "static" { |
| 39 | + if let Ok(lifetime_idx) = self |
| 40 | + .lifetimes |
| 41 | + .binary_search_by_key(&lifetime, |(lt, _)| *lt) |
| 42 | + { |
| 43 | + self.lifetimes[lifetime_idx].1 = true; |
| 44 | + } |
| 45 | + } |
| 46 | + visit::visit_lifetime(self, lifetime) |
| 47 | + } |
| 48 | + |
| 49 | + fn visit_type_reference(&mut self, reference: &'ast syn::TypeReference) { |
| 50 | + if reference.lifetime.is_none() { |
| 51 | + self.errors.push(Rc::new(syn::Error::new_spanned( |
| 52 | + reference, |
| 53 | + "`#[auto_type]` requires named lifetimes", |
| 54 | + ))); |
| 55 | + } |
| 56 | + visit::visit_type_reference(self, reference) |
| 57 | + } |
| 58 | + |
| 59 | + fn visit_type_path(&mut self, type_path: &'ast syn::TypePath) { |
| 60 | + if let Some(path_ident) = type_path.path.get_ident() { |
| 61 | + if let Ok(type_param_idx) = self |
| 62 | + .type_parameters |
| 63 | + .binary_search_by_key(&path_ident, |tp| tp.0) |
| 64 | + { |
| 65 | + self.type_parameters[type_param_idx].1 = true; |
| 66 | + } |
| 67 | + } |
| 68 | + visit::visit_type_path(self, type_path) |
| 69 | + } |
| 70 | + } |
| 71 | + |
| 72 | + visitor.visit_type(ty); |
| 73 | + |
| 74 | + let generic_params: syn::punctuated::Punctuated<syn::GenericParam, _> = generics |
| 75 | + .params |
| 76 | + .iter() |
| 77 | + .filter_map(|param| match param { |
| 78 | + syn::GenericParam::Lifetime(lt) |
| 79 | + if visitor |
| 80 | + .lifetimes |
| 81 | + .binary_search(&(<.lifetime, true)) |
| 82 | + .is_ok() => |
| 83 | + { |
| 84 | + let lt = <.lifetime; |
| 85 | + Some(parse_quote!(#lt)) |
| 86 | + } |
| 87 | + syn::GenericParam::Type(tp) |
| 88 | + if visitor |
| 89 | + .type_parameters |
| 90 | + .binary_search(&(&tp.ident, true)) |
| 91 | + .is_ok() => |
| 92 | + { |
| 93 | + let ident = &tp.ident; |
| 94 | + Some(parse_quote!(#ident)) |
| 95 | + } |
| 96 | + _ => None::<syn::GenericParam>, |
| 97 | + }) |
| 98 | + .collect(); |
| 99 | + |
| 100 | + // We need to not set the lt_token and gt_token if `params` is empty to get |
| 101 | + // a reasonable error message for the case that there is no lifetime specifier |
| 102 | + // but we need one |
| 103 | + syn::Generics { |
| 104 | + lt_token: (!generic_params.is_empty()).then(Default::default), |
| 105 | + gt_token: (!generic_params.is_empty()).then(Default::default), |
| 106 | + params: generic_params, |
| 107 | + where_clause: None, |
| 108 | + } |
| 109 | +} |
0 commit comments