Upgrade rust/crates/async-trait to 0.1.48 Test: make Change-Id: I898f35cd086b1bd9952eba11d5d59d31f78a5d16
diff --git a/src/expand.rs b/src/expand.rs index fb83df1..e78c6c4 100644 --- a/src/expand.rs +++ b/src/expand.rs
@@ -1,19 +1,23 @@ -use crate::lifetime::{has_async_lifetime, CollectLifetimes}; +use crate::lifetime::CollectLifetimes; use crate::parse::Item; -use crate::receiver::{ - has_self_in_block, has_self_in_sig, has_self_in_where_predicate, ReplaceReceiver, -}; -use proc_macro2::{Span, TokenStream}; +use crate::receiver::{has_self_in_block, has_self_in_sig, mut_pat, ReplaceSelf}; +use proc_macro2::TokenStream; use quote::{format_ident, quote, quote_spanned, ToTokens}; -use std::mem; +use std::collections::BTreeSet as Set; use syn::punctuated::Punctuated; -use syn::visit_mut::VisitMut; +use syn::visit_mut::{self, VisitMut}; use syn::{ - parse_quote, Block, FnArg, GenericParam, Generics, Ident, ImplItem, Lifetime, Pat, PatIdent, - Path, Receiver, ReturnType, Signature, Stmt, Token, TraitItem, Type, TypeParam, TypeParamBound, - WhereClause, + parse_quote, Attribute, Block, FnArg, GenericParam, Generics, Ident, ImplItem, Lifetime, Pat, + PatIdent, Receiver, ReturnType, Signature, Stmt, Token, TraitItem, Type, TypeParamBound, + TypePath, WhereClause, }; +macro_rules! parse_quote_spanned { + ($span:expr=> $($t:tt)*) => { + syn::parse2(quote_spanned!($span=> $($t)*)).unwrap() + }; +} + impl ToTokens for Item { fn to_tokens(&self, tokens: &mut TokenStream) { match self { @@ -26,14 +30,12 @@ #[derive(Clone, Copy)] enum Context<'a> { Trait { - name: &'a Ident, generics: &'a Generics, supertraits: &'a Supertraits, }, Impl { impl_generics: &'a Generics, - receiver: &'a Type, - as_trait: &'a Path, + associated_type_impl_traits: &'a Set<Ident>, }, } @@ -59,7 +61,6 @@ match input { Item::Trait(input) => { let context = Context::Trait { - name: &input.ident, generics: &input.generics, supertraits: &input.supertraits, }; @@ -69,32 +70,40 @@ if sig.asyncness.is_some() { let block = &mut method.default; let mut has_self = has_self_in_sig(sig); + method.attrs.push(parse_quote!(#[must_use])); if let Some(block) = block { has_self |= has_self_in_block(block); - transform_block(context, sig, block, has_self, is_local); - method - .attrs - .push(parse_quote!(#[allow(clippy::used_underscore_binding)])); + transform_block(context, sig, block); + method.attrs.push(lint_suppress_with_body()); + } else { + method.attrs.push(lint_suppress_without_body()); } let has_default = method.default.is_some(); transform_sig(context, sig, has_self, has_default, is_local); - method.attrs.push(parse_quote!(#[must_use])); } } } } Item::Impl(input) => { - let mut lifetimes = CollectLifetimes::new("'impl"); + let mut lifetimes = CollectLifetimes::new("'impl", input.impl_token.span); lifetimes.visit_type_mut(&mut *input.self_ty); lifetimes.visit_path_mut(&mut input.trait_.as_mut().unwrap().1); let params = &input.generics.params; let elided = lifetimes.elided; input.generics.params = parse_quote!(#(#elided,)* #params); + let mut associated_type_impl_traits = Set::new(); + for inner in &input.items { + if let ImplItem::Type(assoc) = inner { + if let Type::ImplTrait(_) = assoc.ty { + associated_type_impl_traits.insert(assoc.ident.clone()); + } + } + } + let context = Context::Impl { impl_generics: &input.generics, - receiver: &input.self_ty, - as_trait: &input.trait_.as_ref().unwrap().1, + associated_type_impl_traits: &associated_type_impl_traits, }; for inner in &mut input.items { if let ImplItem::Method(method) = inner { @@ -102,11 +111,9 @@ if sig.asyncness.is_some() { let block = &mut method.block; let has_self = has_self_in_sig(sig) || has_self_in_block(block); - transform_block(context, sig, block, has_self, is_local); + transform_block(context, sig, block); transform_sig(context, sig, has_self, false, is_local); - method - .attrs - .push(parse_quote!(#[allow(clippy::used_underscore_binding)])); + method.attrs.push(lint_suppress_with_body()); } } } @@ -114,6 +121,26 @@ } } +fn lint_suppress_with_body() -> Attribute { + parse_quote! { + #[allow( + clippy::let_unit_value, + clippy::type_complexity, + clippy::type_repetition_in_bounds, + clippy::used_underscore_binding + )] + } +} + +fn lint_suppress_without_body() -> Attribute { + parse_quote! { + #[allow( + clippy::type_complexity, + clippy::type_repetition_in_bounds + )] + } +} + // Input: // async fn f<T>(&self, x: &T) -> Ret; // @@ -141,7 +168,13 @@ ReturnType::Type(_, ret) => quote!(#ret), }; - let mut lifetimes = CollectLifetimes::new("'life"); + let default_span = sig + .ident + .span() + .join(sig.paren_token.span) + .unwrap_or_else(|| sig.ident.span()); + + let mut lifetimes = CollectLifetimes::new("'life", default_span); for arg in sig.inputs.iter_mut() { match arg { FnArg::Receiver(arg) => lifetimes.visit_receiver_mut(arg), @@ -149,13 +182,6 @@ } } - let where_clause = sig - .generics - .where_clause - .get_or_insert_with(|| WhereClause { - where_token: Default::default(), - predicates: Punctuated::new(), - }); for param in sig .generics .params @@ -165,33 +191,48 @@ match param { GenericParam::Type(param) => { let param = ¶m.ident; - where_clause + let span = param.span(); + where_clause_or_default(&mut sig.generics.where_clause) .predicates - .push(parse_quote!(#param: 'async_trait)); + .push(parse_quote_spanned!(span=> #param: 'async_trait)); } GenericParam::Lifetime(param) => { let param = ¶m.lifetime; - where_clause + let span = param.span(); + where_clause_or_default(&mut sig.generics.where_clause) .predicates - .push(parse_quote!(#param: 'async_trait)); + .push(parse_quote_spanned!(span=> #param: 'async_trait)); } GenericParam::Const(_) => {} } } + + if sig.generics.lt_token.is_none() { + sig.generics.lt_token = Some(Token)); + } + if sig.generics.gt_token.is_none() { + sig.generics.gt_token = Some(Token); + } + for elided in lifetimes.elided { sig.generics.params.push(parse_quote!(#elided)); - where_clause + where_clause_or_default(&mut sig.generics.where_clause) .predicates - .push(parse_quote!(#elided: 'async_trait)); + .push(parse_quote_spanned!(elided.span()=> #elided: 'async_trait)); } - sig.generics.params.push(parse_quote!('async_trait)); + + sig.generics + .params + .push(parse_quote_spanned!(default_span=> 'async_trait)); + if has_self { - let bound: Ident = match sig.inputs.iter().next() { + let bound_span = sig.ident.span(); + let bound = match sig.inputs.iter().next() { Some(FnArg::Receiver(Receiver { reference: Some(_), mutability: None, .. - })) => parse_quote!(Sync), + })) => Ident::new("Sync", bound_span), Some(FnArg::Typed(arg)) if match (arg.pat.as_ref(), arg.ty.as_ref()) { (Pat::Ident(pat), Type::Reference(ty)) => { @@ -200,18 +241,21 @@ _ => false, } => { - parse_quote!(Sync) + Ident::new("Sync", bound_span) } - _ => parse_quote!(Send), + _ => Ident::new("Send", bound_span), }; + let assume_bound = match context { Context::Trait { supertraits, .. } => !has_default || has_bound(supertraits, &bound), Context::Impl { .. } => true, }; + + let where_clause = where_clause_or_default(&mut sig.generics.where_clause); where_clause.predicates.push(if assume_bound || is_local { - parse_quote!(Self: 'async_trait) + parse_quote_spanned!(bound_span=> Self: 'async_trait) } else { - parse_quote!(Self: ::core::marker::#bound + 'async_trait) + parse_quote_spanned!(bound_span=> Self: ::core::marker::#bound + 'async_trait) }); } @@ -226,20 +270,21 @@ ident.by_ref = None; ident.mutability = None; } else { - let positional = positional_arg(i); - *arg.pat = parse_quote!(#positional); + let positional = positional_arg(i, &arg.pat); + let m = mut_pat(&mut arg.pat); + arg.pat = parse_quote!(#m #positional); } } } } + let ret_span = sig.ident.span(); let bounds = if is_local { - quote!('async_trait) + quote_spanned!(ret_span=> 'async_trait) } else { - quote!(::core::marker::Send + 'async_trait) + quote_spanned!(ret_span=> ::core::marker::Send + 'async_trait) }; - - sig.output = parse_quote! { + sig.output = parse_quote_spanned! {ret_span=> -> ::core::pin::Pin<Box< dyn ::core::future::Future<Output = #ret> + #bounds >> @@ -247,247 +292,105 @@ } // Input: -// async fn f<T>(&self, x: &T) -> Ret { -// self + x +// async fn f<T>(&self, x: &T, (a, b): (A, B)) -> Ret { +// self + x + a + b // } // // Output: -// async fn f<T, AsyncTrait>(_self: &AsyncTrait, x: &T) -> Ret { -// _self + x -// } -// Box::pin(async_trait_method::<T, Self>(self, x)) -fn transform_block( - context: Context, - sig: &mut Signature, - block: &mut Block, - has_self: bool, - is_local: bool, -) { +// Box::pin(async move { +// let ___ret: Ret = { +// let __self = self; +// let x = x; +// let (a, b) = __arg1; +// +// __self + x + a + b +// }; +// +// ___ret +// }) +fn transform_block(context: Context, sig: &mut Signature, block: &mut Block) { if let Some(Stmt::Item(syn::Item::Verbatim(item))) = block.stmts.first() { if block.stmts.len() == 1 && item.to_string() == ";" { return; } } - let inner = format_ident!("__{}", sig.ident); - let args = sig.inputs.iter().enumerate().map(|(i, arg)| match arg { - FnArg::Receiver(Receiver { self_token, .. }) => quote!(#self_token), - FnArg::Typed(arg) => { - if let Pat::Ident(PatIdent { ident, .. }) = &*arg.pat { - quote!(#ident) - } else { - positional_arg(i).into_token_stream() + let mut self_span = None; + let decls = sig + .inputs + .iter() + .enumerate() + .map(|(i, arg)| match arg { + FnArg::Receiver(Receiver { + self_token, + mutability, + .. + }) => { + let ident = Ident::new("__self", self_token.span); + self_span = Some(self_token.span); + quote!(let #mutability #ident = #self_token;) } - } - }); - - let mut standalone = sig.clone(); - standalone.ident = inner.clone(); - - let generics = match context { - Context::Trait { generics, .. } => generics, - Context::Impl { impl_generics, .. } => impl_generics, - }; - - let mut outer_generics = generics.clone(); - for p in &mut outer_generics.params { - match p { - GenericParam::Type(t) => t.default = None, - GenericParam::Const(c) => c.default = None, - GenericParam::Lifetime(_) => {} - } - } - if !has_self { - if let Some(mut where_clause) = outer_generics.where_clause { - where_clause.predicates = where_clause - .predicates - .into_iter() - .filter_map(|mut pred| { - if has_self_in_where_predicate(&mut pred) { - None + FnArg::Typed(arg) => { + if let Pat::Ident(PatIdent { + ident, mutability, .. + }) = &*arg.pat + { + if ident == "self" { + self_span = Some(ident.span()); + let prefixed = Ident::new("__self", ident.span()); + quote!(let #mutability #prefixed = #ident;) } else { - Some(pred) + quote!(let #mutability #ident = #ident;) } - }) - .collect(); - outer_generics.where_clause = Some(where_clause); - } - } - - let fn_generics = mem::replace(&mut standalone.generics, outer_generics); - standalone.generics.params.extend(fn_generics.params); - if let Some(where_clause) = fn_generics.where_clause { - standalone - .generics - .make_where_clause() - .predicates - .extend(where_clause.predicates); - } - - if has_async_lifetime(&mut standalone, block) { - standalone.generics.params.push(parse_quote!('async_trait)); - } - - let mut types = standalone - .generics - .type_params() - .map(|param| param.ident.clone()) + } else { + let pat = &arg.pat; + let ident = positional_arg(i, pat); + quote!(let #pat = #ident;) + } + } + }) .collect::<Vec<_>>(); - let mut self_bound = None::<TypeParamBound>; - match standalone.inputs.iter_mut().next() { - Some( - arg @ FnArg::Receiver(Receiver { - reference: Some(_), .. - }), - ) => { - let (lifetime, mutability, self_token) = match arg { - FnArg::Receiver(Receiver { - reference: Some((_, lifetime)), - mutability, - self_token, - .. - }) => (lifetime, mutability, self_token), - _ => unreachable!(), - }; - let under_self = Ident::new("_self", self_token.span); - match context { - Context::Trait { .. } => { - self_bound = Some(match mutability { - Some(_) => parse_quote!(::core::marker::Send), - None => parse_quote!(::core::marker::Sync), - }); - *arg = parse_quote! { - #under_self: &#lifetime #mutability AsyncTrait - }; + if let Some(span) = self_span { + let mut replace_self = ReplaceSelf(span); + replace_self.visit_block_mut(block); + } + + let stmts = &block.stmts; + let let_ret = match &mut sig.output { + ReturnType::Default => quote_spanned! {block.brace_token.span=> + #(#decls)* + let _: () = { #(#stmts)* }; + }, + ReturnType::Type(_, ret) => { + if contains_associated_type_impl_trait(context, ret) { + if decls.is_empty() { + quote!(#(#stmts)*) + } else { + quote!(#(#decls)* { #(#stmts)* }) } - Context::Impl { receiver, .. } => { - let mut ty = quote!(#receiver); - if let Type::TraitObject(trait_object) = receiver { - if trait_object.dyn_token.is_none() { - ty = quote!(dyn #ty); - } - if trait_object.bounds.len() > 1 { - ty = quote!((#ty)); - } + } else { + quote_spanned! {block.brace_token.span=> + if let ::core::option::Option::Some(__ret) = ::core::option::Option::None::<#ret> { + return __ret; } - *arg = parse_quote! { - #under_self: &#lifetime #mutability #ty - }; + #(#decls)* + let __ret: #ret = { #(#stmts)* }; + #[allow(unreachable_code)] + __ret } } } - Some(arg @ FnArg::Receiver(_)) => { - let (self_token, mutability) = match arg { - FnArg::Receiver(Receiver { - self_token, - mutability, - .. - }) => (self_token, mutability), - _ => unreachable!(), - }; - let under_self = Ident::new("_self", self_token.span); - match context { - Context::Trait { .. } => { - self_bound = Some(parse_quote!(::core::marker::Send)); - *arg = parse_quote! { - #mutability #under_self: AsyncTrait - }; - } - Context::Impl { receiver, .. } => { - *arg = parse_quote! { - #mutability #under_self: #receiver - }; - } - } - } - Some(FnArg::Typed(arg)) => { - if let Pat::Ident(arg) = &mut *arg.pat { - if arg.ident == "self" { - arg.ident = Ident::new("_self", arg.ident.span()); - } - } - } - _ => {} - } - - if let Context::Trait { name, generics, .. } = context { - if has_self { - let (_, generics, _) = generics.split_for_impl(); - let mut self_param: TypeParam = parse_quote!(AsyncTrait: ?Sized + #name #generics); - if !is_local { - self_param.bounds.extend(self_bound); - } - let count = standalone - .generics - .params - .iter() - .take_while(|param| { - if let GenericParam::Const(_) = param { - false - } else { - true - } - }) - .count(); - standalone - .generics - .params - .insert(count, GenericParam::Type(self_param)); - types.push(Ident::new("Self", Span::call_site())); - } - } - - if let Some(where_clause) = &mut standalone.generics.where_clause { - // Work around an input bound like `where Self::Output: Send` expanding - // to `where <AsyncTrait>::Output: Send` which is illegal syntax because - // `where<T>` is reserved for future use... :( - where_clause.predicates.insert(0, parse_quote!((): Sized)); - } - - let mut replace = match context { - Context::Trait { .. } => ReplaceReceiver::with(parse_quote!(AsyncTrait)), - Context::Impl { - receiver, as_trait, .. - } => ReplaceReceiver::with_as_trait(receiver.clone(), as_trait.clone()), }; - replace.visit_signature_mut(&mut standalone); - replace.visit_block_mut(block); - - let mut generics = types; - let consts = standalone - .generics - .const_params() - .map(|param| param.ident.clone()); - generics.extend(consts); - - let allow_non_snake_case = if sig.ident != sig.ident.to_string().to_lowercase() { - Some(quote!(non_snake_case,)) - } else { - None - }; - - let brace = block.brace_token; - let box_pin = quote_spanned!(brace.span=> { - #[allow( - #allow_non_snake_case - unused_parens, // https://github.com/dtolnay/async-trait/issues/118 - clippy::missing_docs_in_private_items, - clippy::needless_lifetimes, - clippy::ptr_arg, - clippy::trivially_copy_pass_by_ref, - clippy::type_repetition_in_bounds, - clippy::used_underscore_binding, - )] - #standalone #block - Box::pin(#inner::<#(#generics),*>(#(#args),*)) - }); - *block = parse_quote!(#box_pin); - block.brace_token = brace; + let box_pin = quote_spanned!(block.brace_token.span=> + Box::pin(async move { #let_ret }) + ); + block.stmts = parse_quote!(#box_pin); } -fn positional_arg(i: usize) -> Ident { - format_ident!("__arg{}", i) +fn positional_arg(i: usize, pat: &Pat) -> Ident { + use syn::spanned::Spanned; + format_ident!("__arg{}", i, span = pat.span()) } fn has_bound(supertraits: &Supertraits, marker: &Ident) -> bool { @@ -500,3 +403,45 @@ } false } + +fn contains_associated_type_impl_trait(context: Context, ret: &mut Type) -> bool { + struct AssociatedTypeImplTraits<'a> { + set: &'a Set<Ident>, + contains: bool, + } + + impl<'a> VisitMut for AssociatedTypeImplTraits<'a> { + fn visit_type_path_mut(&mut self, ty: &mut TypePath) { + if ty.qself.is_none() + && ty.path.segments.len() == 2 + && ty.path.segments[0].ident == "Self" + && self.set.contains(&ty.path.segments[1].ident) + { + self.contains = true; + } + visit_mut::visit_type_path_mut(self, ty); + } + } + + match context { + Context::Trait { .. } => false, + Context::Impl { + associated_type_impl_traits, + .. + } => { + let mut visit = AssociatedTypeImplTraits { + set: associated_type_impl_traits, + contains: false, + }; + visit.visit_type_mut(ret); + visit.contains + } + } +} + +fn where_clause_or_default(clause: &mut Option<WhereClause>) -> &mut WhereClause { + clause.get_or_insert_with(|| WhereClause { + where_token: Default::default(), + predicates: Punctuated::new(), + }) +}
diff --git a/src/lib.rs b/src/lib.rs index 929af4f..100bee6 100644 --- a/src/lib.rs +++ b/src/lib.rs
@@ -303,7 +303,16 @@ //! let object = &value as &dyn ObjectSafe; //! ``` -#![allow(clippy::match_like_matches_macro)] // matches! requires Rust 1.42 +#![allow( + clippy::default_trait_access, + clippy::doc_markdown, + clippy::if_not_else, + clippy::items_after_statements, + clippy::module_name_repetitions, + clippy::shadow_unrelated, + clippy::similar_names, + clippy::too_many_lines +)] extern crate proc_macro; @@ -312,7 +321,6 @@ mod lifetime; mod parse; mod receiver; -mod respan; use crate::args::Args; use crate::expand::expand;
diff --git a/src/lifetime.rs b/src/lifetime.rs index 9d2066b..ff25d32 100644 --- a/src/lifetime.rs +++ b/src/lifetime.rs
@@ -1,59 +1,43 @@ use proc_macro2::Span; use syn::visit_mut::{self, VisitMut}; -use syn::{Block, GenericArgument, Item, Lifetime, Receiver, Signature, TypeReference}; - -pub fn has_async_lifetime(sig: &mut Signature, block: &mut Block) -> bool { - let mut visitor = HasAsyncLifetime(false); - visitor.visit_signature_mut(sig); - visitor.visit_block_mut(block); - visitor.0 -} - -struct HasAsyncLifetime(bool); - -impl VisitMut for HasAsyncLifetime { - fn visit_lifetime_mut(&mut self, life: &mut Lifetime) { - self.0 |= life.to_string() == "'async_trait"; - } - - fn visit_item_mut(&mut self, _: &mut Item) { - // Do not recurse into nested items. - } -} +use syn::{GenericArgument, Lifetime, Receiver, TypeReference}; pub struct CollectLifetimes { pub elided: Vec<Lifetime>, pub explicit: Vec<Lifetime>, pub name: &'static str, + pub default_span: Span, } impl CollectLifetimes { - pub fn new(name: &'static str) -> Self { + pub fn new(name: &'static str, default_span: Span) -> Self { CollectLifetimes { elided: Vec::new(), explicit: Vec::new(), name, + default_span, } } fn visit_opt_lifetime(&mut self, lifetime: &mut Option<Lifetime>) { match lifetime { - None => *lifetime = Some(self.next_lifetime()), + None => *lifetime = Some(self.next_lifetime(None)), Some(lifetime) => self.visit_lifetime(lifetime), } } fn visit_lifetime(&mut self, lifetime: &mut Lifetime) { if lifetime.ident == "_" { - *lifetime = self.next_lifetime(); + *lifetime = self.next_lifetime(lifetime.span()); } else { self.explicit.push(lifetime.clone()); } } - fn next_lifetime(&mut self) -> Lifetime { + fn next_lifetime<S: Into<Option<Span>>>(&mut self, span: S) -> Lifetime { let name = format!("{}{}", self.name, self.elided.len()); - let life = Lifetime::new(&name, Span::call_site()); + let span = span.into().unwrap_or(self.default_span); + let life = Lifetime::new(&name, span); self.elided.push(life.clone()); life }
diff --git a/src/receiver.rs b/src/receiver.rs index 4273359..64ab65e 100644 --- a/src/receiver.rs +++ b/src/receiver.rs
@@ -1,14 +1,9 @@ -use crate::respan::respan; -use proc_macro2::{Group, Spacing, Span, TokenStream, TokenTree}; -use quote::{quote, quote_spanned}; +use proc_macro2::{Group, Span, TokenStream, TokenTree}; use std::iter::FromIterator; -use std::mem; -use syn::punctuated::Punctuated; use syn::visit_mut::{self, VisitMut}; use syn::{ - parse_quote, Block, Error, ExprPath, ExprStruct, Ident, Item, Macro, PatPath, PatStruct, - PatTupleStruct, Path, PathArguments, QSelf, Receiver, Signature, Token, Type, TypePath, - WherePredicate, + Block, ExprPath, Ident, Item, Macro, Pat, PatIdent, PatPath, Receiver, Signature, Token, + TypePath, }; pub fn has_self_in_sig(sig: &mut Signature) -> bool { @@ -17,12 +12,6 @@ visitor.0 } -pub fn has_self_in_where_predicate(where_predicate: &mut WherePredicate) -> bool { - let mut visitor = HasSelf(false); - visitor.visit_where_predicate_mut(where_predicate); - visitor.0 -} - pub fn has_self_in_block(block: &mut Block) -> bool { let mut visitor = HasSelf(false); visitor.visit_block_mut(block); @@ -37,6 +26,32 @@ }) } +pub fn mut_pat(pat: &mut Pat) -> Option<Token![mut]> { + let mut visitor = HasMutPat(None); + visitor.visit_pat_mut(pat); + visitor.0 +} + +fn contains_fn(tokens: TokenStream) -> bool { + tokens.into_iter().any(|tt| match tt { + TokenTree::Ident(ident) => ident == "fn", + TokenTree::Group(group) => contains_fn(group.stream()), + _ => false, + }) +} + +struct HasMutPat(Option<Token![mut]>); + +impl VisitMut for HasMutPat { + fn visit_pat_ident_mut(&mut self, i: &mut PatIdent) { + if let Some(m) = i.mutability { + self.0 = Some(m); + } else { + visit_mut::visit_pat_ident_mut(self, i); + } + } +} + struct HasSelf(bool); impl VisitMut for HasSelf { @@ -70,225 +85,67 @@ } } -pub struct ReplaceReceiver { - pub with: Type, - pub as_trait: Option<Path>, -} +pub struct ReplaceSelf(pub Span); -impl ReplaceReceiver { - pub fn with(ty: Type) -> Self { - ReplaceReceiver { - with: ty, - as_trait: None, - } - } - - pub fn with_as_trait(ty: Type, as_trait: Path) -> Self { - ReplaceReceiver { - with: ty, - as_trait: Some(as_trait), - } - } - - fn self_ty(&self, span: Span) -> Type { - respan(&self.with, span) - } - - fn self_to_qself_type(&self, qself: &mut Option<QSelf>, path: &mut Path) { - let include_as_trait = true; - self.self_to_qself(qself, path, include_as_trait); - } - - fn self_to_qself_expr(&self, qself: &mut Option<QSelf>, path: &mut Path) { - let include_as_trait = false; - self.self_to_qself(qself, path, include_as_trait); - } - - fn self_to_qself(&self, qself: &mut Option<QSelf>, path: &mut Path, include_as_trait: bool) { - if path.leading_colon.is_some() { - return; - } - - let first = &path.segments[0]; - if first.ident != "Self" || !first.arguments.is_empty() { - return; - } - - if path.segments.len() == 1 { - self.self_to_expr_path(path); - return; - } - - let span = first.ident.span(); - *qself = Some(QSelf { - lt_token: Token, - ty: Box::new(self.self_ty(span)), - position: 0, - as_token: None, - gt_token: Token, - }); - - if include_as_trait && self.as_trait.is_some() { - let as_trait = self.as_trait.as_ref().unwrap().clone(); - path.leading_colon = as_trait.leading_colon; - qself.as_mut().unwrap().position = as_trait.segments.len(); - - let segments = mem::replace(&mut path.segments, as_trait.segments); - path.segments.push_punct(Default::default()); - path.segments.extend(segments.into_pairs().skip(1)); - } else { - path.leading_colon = Some(**path.segments.pairs().next().unwrap().punct().unwrap()); - - let segments = mem::replace(&mut path.segments, Punctuated::new()); - path.segments = segments.into_pairs().skip(1).collect(); - } - } - - fn self_to_expr_path(&self, path: &mut Path) { - if path.leading_colon.is_some() { - return; - } - - let first = &path.segments[0]; - if first.ident != "Self" || !first.arguments.is_empty() { - return; - } - - if let Type::Path(self_ty) = self.self_ty(first.ident.span()) { - let variant = mem::replace(path, self_ty.path); - for segment in &mut path.segments { - if let PathArguments::AngleBracketed(bracketed) = &mut segment.arguments { - if bracketed.colon2_token.is_none() && !bracketed.args.is_empty() { - bracketed.colon2_token = Some(Default::default()); - } - } - } - if variant.segments.len() > 1 { - path.segments.push_punct(Default::default()); - path.segments.extend(variant.segments.into_pairs().skip(1)); - } - } else { - let span = path.segments[0].ident.span(); - let msg = "Self type of this impl is unsupported in expression position"; - let error = Error::new(span, msg).to_compile_error(); - *path = parse_quote!(::core::marker::PhantomData::<#error>); - } - } - - fn visit_token_stream(&self, tokens: &mut TokenStream) -> bool { - let mut out = Vec::new(); - let mut modified = false; - let mut iter = tokens.clone().into_iter().peekable(); - while let Some(tt) = iter.next() { - match tt { - TokenTree::Ident(mut ident) => { - modified |= prepend_underscore_to_self(&mut ident); - if ident == "Self" { - modified = true; - if self.as_trait.is_none() { - let ident = Ident::new("AsyncTrait", ident.span()); - out.push(TokenTree::Ident(ident)); - } else { - let self_ty = self.self_ty(ident.span()); - match iter.peek() { - Some(TokenTree::Punct(p)) - if p.as_char() == ':' && p.spacing() == Spacing::Joint => - { - let next = iter.next().unwrap(); - match iter.peek() { - Some(TokenTree::Punct(p)) if p.as_char() == ':' => { - let span = ident.span(); - out.extend(quote_spanned!(span=> <#self_ty>)); - } - _ => out.extend(quote!(#self_ty)), - } - out.push(next); - } - _ => out.extend(quote!(#self_ty)), - } - } - } else { - out.push(TokenTree::Ident(ident)); - } - } - TokenTree::Group(group) => { - let mut content = group.stream(); - modified |= self.visit_token_stream(&mut content); - let mut new = Group::new(group.delimiter(), content); - new.set_span(group.span()); - out.push(TokenTree::Group(new)); - } - other => out.push(other), - } - } +impl ReplaceSelf { + #[cfg_attr(not(self_span_hack), allow(clippy::unused_self))] + fn prepend_underscore_to_self(&self, ident: &mut Ident) -> bool { + let modified = ident == "self"; if modified { - *tokens = TokenStream::from_iter(out); + *ident = Ident::new("__self", ident.span()); + #[cfg(self_span_hack)] + ident.set_span(self.0); } modified } + + fn visit_token_stream(&mut self, tokens: &mut TokenStream) -> bool { + let mut out = Vec::new(); + let mut modified = false; + visit_token_stream_impl(self, tokens.clone(), &mut modified, &mut out); + if modified { + *tokens = TokenStream::from_iter(out); + } + return modified; + + fn visit_token_stream_impl( + visitor: &mut ReplaceSelf, + tokens: TokenStream, + modified: &mut bool, + out: &mut Vec<TokenTree>, + ) { + for tt in tokens { + match tt { + TokenTree::Ident(mut ident) => { + *modified |= visitor.prepend_underscore_to_self(&mut ident); + out.push(TokenTree::Ident(ident)); + } + TokenTree::Group(group) => { + let mut content = group.stream(); + *modified |= visitor.visit_token_stream(&mut content); + let mut new = Group::new(group.delimiter(), content); + new.set_span(group.span()); + out.push(TokenTree::Group(new)); + } + other => out.push(other), + } + } + } + } } -impl VisitMut for ReplaceReceiver { - // `Self` -> `Receiver` - fn visit_type_mut(&mut self, ty: &mut Type) { - if let Type::Path(node) = ty { - if node.qself.is_none() && node.path.is_ident("Self") { - *ty = self.self_ty(node.path.segments[0].ident.span()); - } else { - self.visit_type_path_mut(node); - } - } else { - visit_mut::visit_type_mut(self, ty); - } - } - - // `Self::Assoc` -> `<Receiver>::Assoc` - fn visit_type_path_mut(&mut self, ty: &mut TypePath) { - if ty.qself.is_none() { - self.self_to_qself_type(&mut ty.qself, &mut ty.path); - } - visit_mut::visit_type_path_mut(self, ty); - } - - // `Self::method` -> `<Receiver>::method` - fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) { - if expr.qself.is_none() { - prepend_underscore_to_self(&mut expr.path.segments[0].ident); - self.self_to_qself_expr(&mut expr.qself, &mut expr.path); - } - visit_mut::visit_expr_path_mut(self, expr); - } - - fn visit_expr_struct_mut(&mut self, expr: &mut ExprStruct) { - self.self_to_expr_path(&mut expr.path); - visit_mut::visit_expr_struct_mut(self, expr); - } - - fn visit_pat_path_mut(&mut self, pat: &mut PatPath) { - if pat.qself.is_none() { - self.self_to_qself_expr(&mut pat.qself, &mut pat.path); - } - visit_mut::visit_pat_path_mut(self, pat); - } - - fn visit_pat_struct_mut(&mut self, pat: &mut PatStruct) { - self.self_to_expr_path(&mut pat.path); - visit_mut::visit_pat_struct_mut(self, pat); - } - - fn visit_pat_tuple_struct_mut(&mut self, pat: &mut PatTupleStruct) { - self.self_to_expr_path(&mut pat.path); - visit_mut::visit_pat_tuple_struct_mut(self, pat); +impl VisitMut for ReplaceSelf { + fn visit_ident_mut(&mut self, i: &mut Ident) { + self.prepend_underscore_to_self(i); } fn visit_item_mut(&mut self, i: &mut Item) { - match i { - // Visit `macro_rules!` because locally defined macros can refer to `self`. - Item::Macro(i) if i.mac.path.is_ident("macro_rules") => { + // Visit `macro_rules!` because locally defined macros can refer to + // `self`. Otherwise, do not recurse into nested items. + if let Item::Macro(i) = i { + if i.mac.path.is_ident("macro_rules") { self.visit_macro_mut(&mut i.mac) } - // Otherwise, do not recurse into nested items. - _ => {} } } @@ -303,19 +160,3 @@ } } } - -fn contains_fn(tokens: TokenStream) -> bool { - tokens.into_iter().any(|tt| match tt { - TokenTree::Ident(ident) => ident == "fn", - TokenTree::Group(group) => contains_fn(group.stream()), - _ => false, - }) -} - -fn prepend_underscore_to_self(ident: &mut Ident) -> bool { - let modified = ident == "self"; - if modified { - *ident = Ident::new("_self", ident.span()); - } - modified -}
diff --git a/src/respan.rs b/src/respan.rs deleted file mode 100644 index 38f6612..0000000 --- a/src/respan.rs +++ /dev/null
@@ -1,22 +0,0 @@ -use proc_macro2::{Span, TokenStream}; -use quote::ToTokens; -use syn::parse::Parse; - -pub(crate) fn respan<T>(node: &T, span: Span) -> T -where - T: ToTokens + Parse, -{ - let tokens = node.to_token_stream(); - let respanned = respan_tokens(tokens, span); - syn::parse2(respanned).unwrap() -} - -fn respan_tokens(tokens: TokenStream, span: Span) -> TokenStream { - tokens - .into_iter() - .map(|mut token| { - token.set_span(span); - token - }) - .collect() -}