diff options
Diffstat (limited to 'src/lifetime.rs')
-rw-r--r-- | src/lifetime.rs | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/src/lifetime.rs b/src/lifetime.rs new file mode 100644 index 0000000..1e5a658 --- /dev/null +++ b/src/lifetime.rs @@ -0,0 +1,110 @@ +use proc_macro2::{Span, TokenStream}; +use std::mem; +use syn::visit_mut::{self, VisitMut}; +use syn::{ + parse_quote_spanned, token, Expr, GenericArgument, Lifetime, Receiver, ReturnType, Token, Type, + TypeBareFn, TypeImplTrait, TypeParen, TypePtr, TypeReference, +}; + +pub struct CollectLifetimes { + pub elided: Vec<Lifetime>, + pub explicit: Vec<Lifetime>, +} + +impl CollectLifetimes { + pub fn new() -> Self { + CollectLifetimes { + elided: Vec::new(), + explicit: Vec::new(), + } + } + + fn visit_opt_lifetime(&mut self, reference: Token![&], lifetime: &mut Option<Lifetime>) { + match lifetime { + None => *lifetime = Some(self.next_lifetime(reference.span)), + Some(lifetime) => self.visit_lifetime(lifetime), + } + } + + fn visit_lifetime(&mut self, lifetime: &mut Lifetime) { + if lifetime.ident == "_" { + *lifetime = self.next_lifetime(lifetime.span()); + } else { + self.explicit.push(lifetime.clone()); + } + } + + fn next_lifetime(&mut self, span: Span) -> Lifetime { + let name = format!("'life{}", self.elided.len()); + let life = Lifetime::new(&name, span); + self.elided.push(life.clone()); + life + } +} + +impl VisitMut for CollectLifetimes { + fn visit_receiver_mut(&mut self, arg: &mut Receiver) { + if let Some((reference, lifetime)) = &mut arg.reference { + self.visit_opt_lifetime(*reference, lifetime); + } + } + + fn visit_type_reference_mut(&mut self, ty: &mut TypeReference) { + self.visit_opt_lifetime(ty.and_token, &mut ty.lifetime); + visit_mut::visit_type_reference_mut(self, ty); + } + + fn visit_generic_argument_mut(&mut self, gen: &mut GenericArgument) { + if let GenericArgument::Lifetime(lifetime) = gen { + self.visit_lifetime(lifetime); + } + visit_mut::visit_generic_argument_mut(self, gen); + } +} + +pub struct AddLifetimeToImplTrait; + +impl VisitMut for AddLifetimeToImplTrait { + fn visit_type_impl_trait_mut(&mut self, ty: &mut TypeImplTrait) { + let span = ty.impl_token.span; + let lifetime = parse_quote_spanned!(span=> 'async_trait); + ty.bounds.insert(0, lifetime); + if let Some(punct) = ty.bounds.pairs_mut().next().unwrap().punct_mut() { + punct.span = span; + } + visit_mut::visit_type_impl_trait_mut(self, ty); + } + + fn visit_type_reference_mut(&mut self, ty: &mut TypeReference) { + parenthesize_impl_trait(&mut ty.elem, ty.and_token.span); + visit_mut::visit_type_reference_mut(self, ty); + } + + fn visit_type_ptr_mut(&mut self, ty: &mut TypePtr) { + parenthesize_impl_trait(&mut ty.elem, ty.star_token.span); + visit_mut::visit_type_ptr_mut(self, ty); + } + + fn visit_type_bare_fn_mut(&mut self, ty: &mut TypeBareFn) { + if let ReturnType::Type(arrow, return_type) = &mut ty.output { + parenthesize_impl_trait(return_type, arrow.spans[0]); + } + visit_mut::visit_type_bare_fn_mut(self, ty); + } + + fn visit_expr_mut(&mut self, _e: &mut Expr) { + // Do not recurse into impl Traits inside of an array length expression. + // + // fn outer(arg: [u8; { fn inner(_: impl Trait) {}; 0 }]); + } +} + +fn parenthesize_impl_trait(elem: &mut Type, paren_span: Span) { + if let Type::ImplTrait(_) = *elem { + let placeholder = Type::Verbatim(TokenStream::new()); + *elem = Type::Paren(TypeParen { + paren_token: token::Paren(paren_span), + elem: Box::new(mem::replace(elem, placeholder)), + }); + } +} |