summaryrefslogtreecommitdiff
path: root/src/lifetime.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lifetime.rs')
-rw-r--r--src/lifetime.rs110
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)),
+ });
+ }
+}