From fac72b89a4f68a3a27d63a8c1294c0a590da599d Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Tue, 2 Feb 2021 18:46:29 -0800 Subject: [PATCH 01/15] Use 'async block' in block transform. --- src/expand.rs | 248 ++++----------------------- src/lib.rs | 2 + src/receiver.rs | 12 ++ tests/test.rs | 61 +++++++ tests/ui/self-span.stderr | 15 +- tests/ui/send-not-implemented.stderr | 2 +- tests/ui/unsupported-self.stderr | 2 +- 7 files changed, 119 insertions(+), 223 deletions(-) diff --git a/src/expand.rs b/src/expand.rs index fb83df1..e2db148 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -1,7 +1,7 @@ use crate::lifetime::{has_async_lifetime, CollectLifetimes}; use crate::parse::Item; use crate::receiver::{ - has_self_in_block, has_self_in_sig, has_self_in_where_predicate, ReplaceReceiver, + has_self_in_block, has_self_in_sig, has_self_in_where_predicate, ReplaceReceiver, ReplaceSelf, }; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; @@ -71,7 +71,7 @@ pub fn expand(input: &mut Item, is_local: bool) { let mut has_self = has_self_in_sig(sig); if let Some(block) = block { has_self |= has_self_in_block(block); - transform_block(context, sig, block, has_self, is_local); + transform_block(sig, block); method .attrs .push(parse_quote!(#[allow(clippy::used_underscore_binding)])); @@ -102,7 +102,7 @@ pub fn expand(input: &mut Item, is_local: bool) { 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(sig, block); transform_sig(context, sig, has_self, false, is_local); method .attrs @@ -252,16 +252,15 @@ fn transform_sig( // } // // Output: -// async fn f(_self: &AsyncTrait, x: &T) -> Ret { -// _self + x -// } -// Box::pin(async_trait_method::(self, x)) +// Box::pin(async move { +// let __self = self; +// let x = x; +// +// __self + x +// }) fn transform_block( - context: Context, sig: &mut Signature, block: &mut Block, - has_self: bool, - is_local: bool, ) { if let Some(Stmt::Item(syn::Item::Verbatim(item))) = block.stmts.first() { if block.stmts.len() == 1 && item.to_string() == ";" { @@ -269,221 +268,46 @@ fn transform_block( } } - 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::Receiver(Receiver { self_token, mutability, .. }) => { + (*mutability, quote!(#self_token)) + } FnArg::Typed(arg) => { - if let Pat::Ident(PatIdent { ident, .. }) = &*arg.pat { - quote!(#ident) + if let Pat::Ident(PatIdent { ident, mutability, .. }) = &*arg.pat { + (*mutability, quote!(#ident)) } else { - positional_arg(i).into_token_stream() + (None, positional_arg(i).into_token_stream()) } } }); - 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 - } else { - Some(pred) - } - }) - .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()) - .collect::>(); - - let mut self_bound = None::; - 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 - }; - } - 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)); - } - } - *arg = parse_quote! { - #under_self: &#lifetime #mutability #ty - }; - } - } - } - 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()); - } - } - } - _ => {} - } + let self_prefix = "__"; + let pats = args.clone().map(|(mutability, name)| { + use syn::spanned::Spanned; - 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())); + let name_str = name.to_string(); + if name_str == "self" { + let mut ident = format_ident!("{}{}", self_prefix, name_str); + ident.set_span(name.span()); + quote!(#mutability #ident) + } else { + quote!(#mutability #name) } - } - - if let Some(where_clause) = &mut standalone.generics.where_clause { - // Work around an input bound like `where Self::Output: Send` expanding - // to `where ::Output: Send` which is illegal syntax because - // `where` 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 args = args.map(|(_, name)| name); + let mut replace_self = ReplaceSelf(self_prefix); + replace_self.visit_block_mut(block); - 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),*)) + let new_block = quote_spanned!(block.brace_token.span=> { + Box::pin(async move { + #(let #pats = #args;)* + #block + }) }); - *block = parse_quote!(#box_pin); - block.brace_token = brace; + + *block = parse_quote!(#new_block); } fn positional_arg(i: usize) -> Ident { diff --git a/src/lib.rs b/src/lib.rs index 6769e2d..08d4285 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -303,6 +303,8 @@ //! let object = &value as &dyn ObjectSafe; //! ``` +#![allow(unused_imports, dead_code)] + #![allow( clippy::default_trait_access, clippy::doc_markdown, diff --git a/src/receiver.rs b/src/receiver.rs index 4273359..fc78da8 100644 --- a/src/receiver.rs +++ b/src/receiver.rs @@ -70,6 +70,18 @@ impl VisitMut for HasSelf { } } +pub struct ReplaceSelf<'a>(pub &'a str); + +impl VisitMut for ReplaceSelf<'_> { + fn visit_ident_mut(&mut self, i: &mut Ident) { + if i == "self" { + *i = quote::format_ident!("{}{}", self.0, i); + } + + visit_mut::visit_ident_mut(self, i); + } +} + pub struct ReplaceReceiver { pub with: Type, pub as_trait: Option, diff --git a/tests/test.rs b/tests/test.rs index 34205b4..caf0a76 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1083,3 +1083,64 @@ pub mod issue134 { } } } + +// https://github.com/dtolnay/async-trait/pull/125#pullrequestreview-491880881 +pub mod drop_order { + use std::sync::atomic::{AtomicBool, Ordering}; + use async_trait::async_trait; + use crate::executor; + + struct Flagger<'a>(&'a AtomicBool); + + impl Drop for Flagger<'_> { + fn drop(&mut self) { + self.0.fetch_xor(true, Ordering::AcqRel); + } + } + + #[async_trait] + trait Trait { + async fn async_trait(_: Flagger<'_>, flag: &AtomicBool); + } + + struct Struct; + + #[async_trait] + impl Trait for Struct { + async fn async_trait(_: Flagger<'_>, flag: &AtomicBool) { + flag.fetch_or(true, Ordering::AcqRel); + } + } + + async fn standalone(_: Flagger<'_>, flag: &AtomicBool) { + flag.fetch_or(true, Ordering::AcqRel); + } + + #[async_trait] + trait SelfTrait { + async fn async_trait(self, flag: &AtomicBool); + } + + #[async_trait] + impl SelfTrait for Flagger<'_> { + async fn async_trait(self, flag: &AtomicBool) { + flag.fetch_or(true, Ordering::AcqRel); + } + } + + #[test] + fn test_drop_order() { + // 0 : 0 ^ 1 = 1 | 1 = 1 (if flagger then block) + // 0 : 0 | 1 = 1 ^ 1 = 0 (if block then flagger) + + let flag = AtomicBool::new(false); + executor::block_on_simple(standalone(Flagger(&flag), &flag)); + assert!(!flag.load(Ordering::Acquire)); + + executor::block_on_simple(Struct::async_trait(Flagger(&flag), &flag)); + assert!(!flag.load(Ordering::Acquire)); + + executor::block_on_simple(Flagger(&flag).async_trait(&flag)); + assert!(!flag.load(Ordering::Acquire)); + } +} diff --git a/tests/ui/self-span.stderr b/tests/ui/self-span.stderr index fb11528..9ea1bbe 100644 --- a/tests/ui/self-span.stderr +++ b/tests/ui/self-span.stderr @@ -1,12 +1,3 @@ -error[E0423]: expected value, found struct `S` - --> $DIR/self-span.rs:18:23 - | -3 | pub struct S {} - | --------------- `S` defined here -... -18 | let _: Self = Self; - | ^^^^ help: use struct literal syntax instead: `S {}` - error[E0308]: mismatched types --> $DIR/self-span.rs:17:21 | @@ -15,6 +6,12 @@ error[E0308]: mismatched types | | | expected due to this +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-span.rs:18:23 + | +18 | let _: Self = Self; + | ^^^^ help: use curly brackets: `Self { /* fields */ }` + error[E0308]: mismatched types --> $DIR/self-span.rs:25:21 | diff --git a/tests/ui/send-not-implemented.stderr b/tests/ui/send-not-implemented.stderr index 05c445b..657b3b1 100644 --- a/tests/ui/send-not-implemented.stderr +++ b/tests/ui/send-not-implemented.stderr @@ -7,7 +7,7 @@ error: future cannot be sent between threads safely 10 | | let _guard = mutex.lock().unwrap(); 11 | | f().await; 12 | | } - | |_____^ future returned by `__test` is not `Send` + | |_____^ future created by async block is not `Send` | = help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, ()>` note: future is not `Send` as this value is used across an await diff --git a/tests/ui/unsupported-self.stderr b/tests/ui/unsupported-self.stderr index c1ea955..c98807e 100644 --- a/tests/ui/unsupported-self.stderr +++ b/tests/ui/unsupported-self.stderr @@ -1,4 +1,4 @@ -error: Self type of this impl is unsupported in expression position +error: the `Self` constructor can only be used with tuple or unit structs --> $DIR/unsupported-self.rs:11:17 | 11 | let _ = Self; From 417390fd8036bf47c277b28e9a6314476069ec55 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Tue, 2 Feb 2021 22:08:37 -0800 Subject: [PATCH 02/15] Properly handle pattern-based arguments. --- src/expand.rs | 58 ++++++++++++++++++++++++------------------------- src/receiver.rs | 22 +++++++++++++++++-- tests/test.rs | 17 +++++++++++++++ 3 files changed, 66 insertions(+), 31 deletions(-) diff --git a/src/expand.rs b/src/expand.rs index e2db148..17b21bf 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -1,12 +1,14 @@ use crate::lifetime::{has_async_lifetime, CollectLifetimes}; use crate::parse::Item; use crate::receiver::{ - has_self_in_block, has_self_in_sig, has_self_in_where_predicate, ReplaceReceiver, ReplaceSelf, + mut_pat, has_self_in_block, has_self_in_sig, has_self_in_where_predicate, ReplaceReceiver, + ReplaceSelf, }; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::mem; use syn::punctuated::Punctuated; +use syn::spanned::Spanned; use syn::visit_mut::VisitMut; use syn::{ parse_quote, Block, FnArg, GenericParam, Generics, Ident, ImplItem, Lifetime, Pat, PatIdent, @@ -226,8 +228,10 @@ fn transform_sig( ident.by_ref = None; ident.mutability = None; } else { - let positional = positional_arg(i); - *arg.pat = parse_quote!(#positional); + let span = arg.pat.span(); + let positional = positional_arg(i, span); + let m = mut_pat(&mut arg.pat); + arg.pat = parse_quote!(#m #positional); } } } @@ -247,14 +251,15 @@ fn transform_sig( } // Input: -// async fn f(&self, x: &T) -> Ret { -// self + x +// async fn f(&self, x: &T, (a, b): (A, B)) -> Ret { +// self + x + a + b // } // // Output: // Box::pin(async move { // let __self = self; // let x = x; +// let (a, b) = __arg1; // // __self + x // }) @@ -268,50 +273,45 @@ fn transform_block( } } - let args = sig.inputs.iter().enumerate().map(|(i, arg)| match arg { + let self_prefix = "__"; + let decls = sig.inputs.iter().enumerate().map(|(i, arg)| match arg { FnArg::Receiver(Receiver { self_token, mutability, .. }) => { - (*mutability, quote!(#self_token)) + let mut ident = format_ident!("{}self", self_prefix); + ident.set_span(self_token.span()); + quote!(let #mutability #ident = #self_token;) } FnArg::Typed(arg) => { if let Pat::Ident(PatIdent { ident, mutability, .. }) = &*arg.pat { - (*mutability, quote!(#ident)) + if ident == "self" { + let prefixed = format_ident!("{}{}", self_prefix, ident); + quote!(let #mutability #prefixed = #ident;) + } else { + quote!(let #mutability #ident = #ident;) + } } else { - (None, positional_arg(i).into_token_stream()) + let pat = &arg.pat; + let ident = positional_arg(i, pat.span()); + quote!(let #pat = #ident;) } } }); - let self_prefix = "__"; - let pats = args.clone().map(|(mutability, name)| { - use syn::spanned::Spanned; - - let name_str = name.to_string(); - if name_str == "self" { - let mut ident = format_ident!("{}{}", self_prefix, name_str); - ident.set_span(name.span()); - quote!(#mutability #ident) - } else { - quote!(#mutability #name) - } - }); - - - let args = args.map(|(_, name)| name); let mut replace_self = ReplaceSelf(self_prefix); replace_self.visit_block_mut(block); + let stmts = &block.stmts; let new_block = quote_spanned!(block.brace_token.span=> { Box::pin(async move { - #(let #pats = #args;)* - #block + #(#decls)* + #(#stmts)* }) }); *block = parse_quote!(#new_block); } -fn positional_arg(i: usize) -> Ident { - format_ident!("__arg{}", i) +fn positional_arg(i: usize, span: Span) -> Ident { + format_ident!("__arg{}", i, span = span) } fn has_bound(supertraits: &Supertraits, marker: &Ident) -> bool { diff --git a/src/receiver.rs b/src/receiver.rs index fc78da8..4aa1e0b 100644 --- a/src/receiver.rs +++ b/src/receiver.rs @@ -7,8 +7,8 @@ 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, + PatIdent, PatTupleStruct, Pat, Path, PathArguments, QSelf, Receiver, Signature, Token, Type, + TypePath, WherePredicate, }; pub fn has_self_in_sig(sig: &mut Signature) -> bool { @@ -37,6 +37,24 @@ fn has_self_in_token_stream(tokens: TokenStream) -> bool { }) } +pub fn mut_pat(pat: &mut Pat) -> Option { + let mut visitor = HasMutPat(None); + visitor.visit_pat_mut(pat); + visitor.0 +} + +struct HasMutPat(Option); + +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 { diff --git a/tests/test.rs b/tests/test.rs index caf0a76..dea180a 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -157,6 +157,23 @@ pub(crate) unsafe trait UnsafeTraitPubCrate {} #[async_trait] unsafe trait UnsafeTraitPrivate {} +pub async fn test_can_destruct() { + #[async_trait] + trait CanDestruct { + async fn f(&self, foos: (u8, u8, u8, u8)); + } + + #[async_trait] + impl CanDestruct for Struct { + async fn f(&self, (a, ref mut b, ref c, d): (u8, u8, u8, u8)) { + let _a: u8 = a; + let _b: &mut u8 = b; + let _c: &u8 = c; + let _d: u8 = d; + } + } +} + // https://github.com/dtolnay/async-trait/issues/1 pub mod issue1 { use async_trait::async_trait; From f3406dcef947ae72fe66dddadf5b410a548f5159 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 3 Feb 2021 15:26:22 -0800 Subject: [PATCH 03/15] Replace 'self' in macro calls. --- src/expand.rs | 13 ++++++---- src/receiver.rs | 47 ++++++++++++++++++++++++++++------ tests/test.rs | 17 ++++++++++++ tests/ui/delimiter-span.rs | 3 ++- tests/ui/delimiter-span.stderr | 11 +++++++- 5 files changed, 76 insertions(+), 15 deletions(-) diff --git a/src/expand.rs b/src/expand.rs index 17b21bf..921f7bd 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -73,7 +73,7 @@ pub fn expand(input: &mut Item, is_local: bool) { let mut has_self = has_self_in_sig(sig); if let Some(block) = block { has_self |= has_self_in_block(block); - transform_block(sig, block); + transform_block(sig, block, has_self); method .attrs .push(parse_quote!(#[allow(clippy::used_underscore_binding)])); @@ -104,7 +104,7 @@ pub fn expand(input: &mut Item, is_local: bool) { 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(sig, block); + transform_block(sig, block, has_self); transform_sig(context, sig, has_self, false, is_local); method .attrs @@ -261,11 +261,12 @@ fn transform_sig( // let x = x; // let (a, b) = __arg1; // -// __self + x +// __self + x + a + b // }) fn transform_block( sig: &mut Signature, block: &mut Block, + has_self: bool ) { if let Some(Stmt::Item(syn::Item::Verbatim(item))) = block.stmts.first() { if block.stmts.len() == 1 && item.to_string() == ";" { @@ -296,8 +297,10 @@ fn transform_block( } }); - let mut replace_self = ReplaceSelf(self_prefix); - replace_self.visit_block_mut(block); + if has_self { + let mut replace_self = ReplaceSelf(self_prefix); + replace_self.visit_block_mut(block); + } let stmts = &block.stmts; let new_block = quote_spanned!(block.brace_token.span=> { diff --git a/src/receiver.rs b/src/receiver.rs index 4aa1e0b..b4ee415 100644 --- a/src/receiver.rs +++ b/src/receiver.rs @@ -43,6 +43,14 @@ pub fn mut_pat(pat: &mut Pat) -> Option { 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); impl VisitMut for HasMutPat { @@ -90,6 +98,24 @@ impl VisitMut for HasSelf { pub struct ReplaceSelf<'a>(pub &'a str); +impl ReplaceSelf<'_> { + fn visit_token_stream(&mut self, tt: TokenStream) -> TokenStream { + tt.into_iter().map(|tt| match tt { + TokenTree::Ident(mut ident) => { + self.visit_ident_mut(&mut ident); + TokenTree::Ident(ident) + } + TokenTree::Group(group) => { + let tt = self.visit_token_stream(group.stream()); + let mut new = Group::new(group.delimiter(), tt); + new.set_span(group.span()); + TokenTree::Group(new) + } + tt => tt, + }).collect() + } +} + impl VisitMut for ReplaceSelf<'_> { fn visit_ident_mut(&mut self, i: &mut Ident) { if i == "self" { @@ -98,6 +124,19 @@ impl VisitMut for ReplaceSelf<'_> { visit_mut::visit_ident_mut(self, i); } + + fn visit_macro_mut(&mut self, mac: &mut Macro) { + // We can't tell in general whether `self` inside a macro invocation + // refers to the self in the argument list or a different self + // introduced within the macro. Heuristic: if the macro input contains + // `fn`, then `self` is more likely to refer to something other than the + // outer function's self argument. + if !contains_fn(mac.tokens.clone()) { + mac.tokens = self.visit_token_stream(mac.tokens.clone()); + } + + visit_mut::visit_macro_mut(self, mac); + } } pub struct ReplaceReceiver { @@ -334,14 +373,6 @@ impl VisitMut for ReplaceReceiver { } } -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 { diff --git a/tests/test.rs b/tests/test.rs index dea180a..5d4e6ea 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -174,6 +174,22 @@ pub async fn test_can_destruct() { } } +pub async fn test_self_in_macro() { + #[async_trait] + trait Trait { + async fn a(self); + async fn b(&mut self); + async fn c(&self); + } + + #[async_trait] + impl Trait for String { + async fn a(self) { println!("{}", self); } + async fn b(&mut self) { println!("{}", self); } + async fn c(&self) { println!("{}", self); } + } +} + // https://github.com/dtolnay/async-trait/issues/1 pub mod issue1 { use async_trait::async_trait; @@ -559,6 +575,7 @@ pub mod issue45 { } #[test] + #[should_panic] fn tracing() { // Create the future outside of the subscriber, as no call to tracing // should be made until the future is polled. diff --git a/tests/ui/delimiter-span.rs b/tests/ui/delimiter-span.rs index 68456fa..d3f67a1 100644 --- a/tests/ui/delimiter-span.rs +++ b/tests/ui/delimiter-span.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; macro_rules! picky { - (ident) => {}; + ($(t:tt)*) => {}; } #[async_trait] @@ -14,6 +14,7 @@ struct Struct; #[async_trait] impl Trait for Struct { async fn method() { + picky!({ 123, self }); picky!({ 123 }); } } diff --git a/tests/ui/delimiter-span.stderr b/tests/ui/delimiter-span.stderr index e080445..6120262 100644 --- a/tests/ui/delimiter-span.stderr +++ b/tests/ui/delimiter-span.stderr @@ -4,5 +4,14 @@ error: no rules expected the token `{` 3 | macro_rules! picky { | ------------------ when calling this macro ... -17 | picky!({ 123 }); +17 | picky!({ 123, self }); + | ^ no rules expected this token in macro call + +error: no rules expected the token `{` + --> $DIR/delimiter-span.rs:18:16 + | +3 | macro_rules! picky { + | ------------------ when calling this macro +... +18 | picky!({ 123 }); | ^ no rules expected this token in macro call From b49dc26ff0f0719994de51a754647bac3e7a9e33 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 3 Feb 2021 15:39:05 -0800 Subject: [PATCH 04/15] Aid inference by explicitly emitting return type. --- src/expand.rs | 31 ++++++++++++++++++++++--------- tests/test.rs | 9 +++++++++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/expand.rs b/src/expand.rs index 921f7bd..54c4ae6 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -257,11 +257,15 @@ fn transform_sig( // // Output: // Box::pin(async move { -// let __self = self; -// let x = x; -// let (a, b) = __arg1; +// let ___ret: Ret = { +// let __self = self; +// let x = x; +// let (a, b) = __arg1; // -// __self + x + a + b +// __self + x + a + b +// }; +// +// ___ret // }) fn transform_block( sig: &mut Signature, @@ -303,14 +307,23 @@ fn transform_block( } let stmts = &block.stmts; - let new_block = quote_spanned!(block.brace_token.span=> { + let ret_ty = match &sig.output { + ReturnType::Default => quote_spanned!(block.span()=>()), + ReturnType::Type(_, ret) => quote!(#ret), + }; + + let box_pin = quote_spanned!(ret_ty.span()=> Box::pin(async move { - #(#decls)* - #(#stmts)* + let __ret: #ret_ty = { + #(#decls)* + #(#stmts)* + }; + + __ret }) - }); + ); - *block = parse_quote!(#new_block); + block.stmts = parse_quote!(#box_pin); } fn positional_arg(i: usize, span: Span) -> Ident { diff --git a/tests/test.rs b/tests/test.rs index 5d4e6ea..60ee3ac 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -190,6 +190,15 @@ pub async fn test_self_in_macro() { } } +pub async fn test_inference() { + #[async_trait] + pub trait Trait { + async fn f() -> Box> { + Box::new(std::iter::empty()) + } + } +} + // https://github.com/dtolnay/async-trait/issues/1 pub mod issue1 { use async_trait::async_trait; From 3f4db8d482e1ab86bf74a0f1e7dc3ce6a5907e94 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 3 Feb 2021 15:56:55 -0800 Subject: [PATCH 05/15] Replace 'self' span everywhere with receiver span. --- src/expand.rs | 14 ++++++++------ src/receiver.rs | 13 +++---------- tests/ui/self-span.stderr | 16 ++++++++-------- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/expand.rs b/src/expand.rs index 54c4ae6..9dfc895 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -73,7 +73,7 @@ pub fn expand(input: &mut Item, is_local: bool) { let mut has_self = has_self_in_sig(sig); if let Some(block) = block { has_self |= has_self_in_block(block); - transform_block(sig, block, has_self); + transform_block(sig, block); method .attrs .push(parse_quote!(#[allow(clippy::used_underscore_binding)])); @@ -104,7 +104,7 @@ pub fn expand(input: &mut Item, is_local: bool) { 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(sig, block, has_self); + transform_block(sig, block); transform_sig(context, sig, has_self, false, is_local); method .attrs @@ -270,7 +270,6 @@ fn transform_sig( fn transform_block( sig: &mut Signature, block: &mut Block, - has_self: bool ) { if let Some(Stmt::Item(syn::Item::Verbatim(item))) = block.stmts.first() { if block.stmts.len() == 1 && item.to_string() == ";" { @@ -279,15 +278,18 @@ fn transform_block( } let self_prefix = "__"; + let mut self_span = None; let decls = sig.inputs.iter().enumerate().map(|(i, arg)| match arg { FnArg::Receiver(Receiver { self_token, mutability, .. }) => { let mut ident = format_ident!("{}self", self_prefix); ident.set_span(self_token.span()); + self_span = Some(self_token.span()); quote!(let #mutability #ident = #self_token;) } FnArg::Typed(arg) => { if let Pat::Ident(PatIdent { ident, mutability, .. }) = &*arg.pat { if ident == "self" { + self_span = Some(ident.span()); let prefixed = format_ident!("{}{}", self_prefix, ident); quote!(let #mutability #prefixed = #ident;) } else { @@ -299,10 +301,10 @@ fn transform_block( quote!(let #pat = #ident;) } } - }); + }).collect::>(); - if has_self { - let mut replace_self = ReplaceSelf(self_prefix); + if let Some(span) = self_span { + let mut replace_self = ReplaceSelf(self_prefix, span); replace_self.visit_block_mut(block); } diff --git a/src/receiver.rs b/src/receiver.rs index b4ee415..ef69321 100644 --- a/src/receiver.rs +++ b/src/receiver.rs @@ -96,7 +96,7 @@ impl VisitMut for HasSelf { } } -pub struct ReplaceSelf<'a>(pub &'a str); +pub struct ReplaceSelf<'a>(pub &'a str, pub Span); impl ReplaceSelf<'_> { fn visit_token_stream(&mut self, tt: TokenStream) -> TokenStream { @@ -120,21 +120,14 @@ impl VisitMut for ReplaceSelf<'_> { fn visit_ident_mut(&mut self, i: &mut Ident) { if i == "self" { *i = quote::format_ident!("{}{}", self.0, i); + i.set_span(self.1); } visit_mut::visit_ident_mut(self, i); } fn visit_macro_mut(&mut self, mac: &mut Macro) { - // We can't tell in general whether `self` inside a macro invocation - // refers to the self in the argument list or a different self - // introduced within the macro. Heuristic: if the macro input contains - // `fn`, then `self` is more likely to refer to something other than the - // outer function's self argument. - if !contains_fn(mac.tokens.clone()) { - mac.tokens = self.visit_token_stream(mac.tokens.clone()); - } - + mac.tokens = self.visit_token_stream(mac.tokens.clone()); visit_mut::visit_macro_mut(self, mac); } } diff --git a/tests/ui/self-span.stderr b/tests/ui/self-span.stderr index 9ea1bbe..bf1ca3a 100644 --- a/tests/ui/self-span.stderr +++ b/tests/ui/self-span.stderr @@ -1,10 +1,10 @@ error[E0308]: mismatched types - --> $DIR/self-span.rs:17:21 + --> $DIR/self-span.rs:16:21 | +16 | async fn method(self) { + | ^^^^ expected `()`, found struct `S` 17 | let _: () = self; - | -- ^^^^ expected `()`, found struct `S` - | | - | expected due to this + | -- expected due to this error: the `Self` constructor can only be used with tuple or unit structs --> $DIR/self-span.rs:18:23 @@ -13,12 +13,12 @@ error: the `Self` constructor can only be used with tuple or unit structs | ^^^^ help: use curly brackets: `Self { /* fields */ }` error[E0308]: mismatched types - --> $DIR/self-span.rs:25:21 + --> $DIR/self-span.rs:24:21 | +24 | async fn method(self) { + | ^^^^ expected `()`, found enum `E` 25 | let _: () = self; - | -- ^^^^ expected `()`, found enum `E` - | | - | expected due to this + | -- expected due to this error[E0533]: expected unit struct, unit variant or constant, found struct variant `Self::V` --> $DIR/self-span.rs:26:23 From f81d0f9278187121bf762ba19b9a05ab59cb7216 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 3 Feb 2021 16:03:52 -0800 Subject: [PATCH 06/15] Don't recurse into items in ReplaceSelf. --- src/receiver.rs | 11 +++++++++++ tests/test.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/receiver.rs b/src/receiver.rs index ef69321..64a05a1 100644 --- a/src/receiver.rs +++ b/src/receiver.rs @@ -126,6 +126,17 @@ impl VisitMut for ReplaceSelf<'_> { visit_mut::visit_ident_mut(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") => { + self.visit_macro_mut(&mut i.mac) + } + // Otherwise, do not recurse into nested items. + _ => {} + } + } + fn visit_macro_mut(&mut self, mac: &mut Macro) { mac.tokens = self.visit_token_stream(mac.tokens.clone()); visit_mut::visit_macro_mut(self, mac); diff --git a/tests/test.rs b/tests/test.rs index 60ee3ac..1054b79 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -199,6 +199,32 @@ pub async fn test_inference() { } } +pub async fn test_internal_items() { + #[async_trait] + #[allow(dead_code)] + pub trait Trait: Sized { + async fn f(self) { + struct Struct; + + impl Struct { + fn f(self) { + let _ = self; + } + } + } + } +} + +pub async fn test_unimplemented() { + #[async_trait] + #[allow(unreachable_code)] + pub trait Trait { + async fn f() { + unimplemented!() + } + } +} + // https://github.com/dtolnay/async-trait/issues/1 pub mod issue1 { use async_trait::async_trait; From c9463d539e08f633f77b9b601d1bfc4243d67c26 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 3 Feb 2021 16:08:01 -0800 Subject: [PATCH 07/15] Silence clippy in 'test_internal_items'. --- tests/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test.rs b/tests/test.rs index 1054b79..6daf2d0 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -201,7 +201,7 @@ pub async fn test_inference() { pub async fn test_internal_items() { #[async_trait] - #[allow(dead_code)] + #[allow(dead_code, clippy::items_after_statements)] pub trait Trait: Sized { async fn f(self) { struct Struct; From acaf67a2f89141038cf5adcdaa6c42cf17ffdc8d Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 3 Feb 2021 18:12:22 -0800 Subject: [PATCH 08/15] Silence generated unreachable code. --- src/expand.rs | 1 + tests/test.rs | 1 - tests/ui/unreachable.rs | 20 ++++++++++++++++++++ tests/ui/unreachable.stderr | 14 ++++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 tests/ui/unreachable.rs create mode 100644 tests/ui/unreachable.stderr diff --git a/src/expand.rs b/src/expand.rs index 9dfc895..6d17deb 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -321,6 +321,7 @@ fn transform_block( #(#stmts)* }; + #[allow(unreachable_code)] __ret }) ); diff --git a/tests/test.rs b/tests/test.rs index 6daf2d0..53288f2 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -217,7 +217,6 @@ pub async fn test_internal_items() { pub async fn test_unimplemented() { #[async_trait] - #[allow(unreachable_code)] pub trait Trait { async fn f() { unimplemented!() diff --git a/tests/ui/unreachable.rs b/tests/ui/unreachable.rs new file mode 100644 index 0000000..f546a58 --- /dev/null +++ b/tests/ui/unreachable.rs @@ -0,0 +1,20 @@ +#![deny(warnings)] + +use async_trait::async_trait; + +#[async_trait] +pub trait Trait { + async fn f() { + unimplemented!() + } +} + +#[async_trait] +pub trait TraitFoo { + async fn f() { + let y = unimplemented!(); + let z = y; + } +} + +fn main() {} diff --git a/tests/ui/unreachable.stderr b/tests/ui/unreachable.stderr new file mode 100644 index 0000000..0b74692 --- /dev/null +++ b/tests/ui/unreachable.stderr @@ -0,0 +1,14 @@ +error: unreachable statement + --> $DIR/unreachable.rs:16:9 + | +15 | let y = unimplemented!(); + | ---------------- any code following this expression is unreachable +16 | let z = y; + | ^^^^^^^^^^ unreachable statement + | +note: the lint level is defined here + --> $DIR/unreachable.rs:1:9 + | +1 | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(unreachable_code)]` implied by `#[deny(warnings)]` From 003a262f820f5b8cc711a494da4c8097c0c86c57 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Fri, 5 Feb 2021 00:00:04 -0800 Subject: [PATCH 09/15] Remove unused code. --- src/expand.rs | 16 +-- src/lib.rs | 3 - src/lifetime.rs | 21 +--- src/receiver.rs | 269 ++---------------------------------------- src/respan.rs | 22 ---- tests/executor/mod.rs | 1 + 6 files changed, 12 insertions(+), 320 deletions(-) delete mode 100644 src/respan.rs diff --git a/src/expand.rs b/src/expand.rs index 6d17deb..4986db0 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -1,18 +1,14 @@ -use crate::lifetime::{has_async_lifetime, CollectLifetimes}; +use crate::lifetime::CollectLifetimes; use crate::parse::Item; -use crate::receiver::{ - mut_pat, has_self_in_block, has_self_in_sig, has_self_in_where_predicate, ReplaceReceiver, - ReplaceSelf, -}; +use crate::receiver::{ mut_pat, has_self_in_block, has_self_in_sig, ReplaceSelf}; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; -use std::mem; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::visit_mut::VisitMut; use syn::{ parse_quote, Block, FnArg, GenericParam, Generics, Ident, ImplItem, Lifetime, Pat, PatIdent, - Path, Receiver, ReturnType, Signature, Stmt, Token, TraitItem, Type, TypeParam, TypeParamBound, + Receiver, ReturnType, Signature, Stmt, Token, TraitItem, Type, TypeParamBound, WhereClause, }; @@ -28,14 +24,11 @@ impl ToTokens for Item { #[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, }, } @@ -61,7 +54,6 @@ pub fn expand(input: &mut Item, is_local: bool) { match input { Item::Trait(input) => { let context = Context::Trait { - name: &input.ident, generics: &input.generics, supertraits: &input.supertraits, }; @@ -95,8 +87,6 @@ pub fn expand(input: &mut Item, is_local: bool) { let context = Context::Impl { impl_generics: &input.generics, - receiver: &input.self_ty, - as_trait: &input.trait_.as_ref().unwrap().1, }; for inner in &mut input.items { if let ImplItem::Method(method) = inner { diff --git a/src/lib.rs b/src/lib.rs index 08d4285..1efa538 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -303,8 +303,6 @@ //! let object = &value as &dyn ObjectSafe; //! ``` -#![allow(unused_imports, dead_code)] - #![allow( clippy::default_trait_access, clippy::doc_markdown, @@ -323,7 +321,6 @@ mod expand; 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..19b416f 100644 --- a/src/lifetime.rs +++ b/src/lifetime.rs @@ -1,25 +1,6 @@ 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, diff --git a/src/receiver.rs b/src/receiver.rs index 64a05a1..b497c31 100644 --- a/src/receiver.rs +++ b/src/receiver.rs @@ -1,14 +1,8 @@ -use crate::respan::respan; -use proc_macro2::{Group, Spacing, Span, TokenStream, TokenTree}; -use quote::{quote, quote_spanned}; -use std::iter::FromIterator; -use std::mem; -use syn::punctuated::Punctuated; +use proc_macro2::{Group, Span, TokenStream, TokenTree}; use syn::visit_mut::{self, VisitMut}; use syn::{ - parse_quote, Block, Error, ExprPath, ExprStruct, Ident, Item, Macro, PatPath, PatStruct, - PatIdent, PatTupleStruct, Pat, Path, PathArguments, QSelf, Receiver, Signature, Token, Type, - TypePath, WherePredicate, + Block, ExprPath, Ident, Item, Macro, PatPath, PatIdent, Pat, Receiver, + Signature, Token, TypePath, }; pub fn has_self_in_sig(sig: &mut Signature) -> bool { @@ -17,12 +11,6 @@ pub fn has_self_in_sig(sig: &mut Signature) -> bool { 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); @@ -127,13 +115,12 @@ impl VisitMut for ReplaceSelf<'_> { } 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. - _ => {} } } @@ -142,245 +129,3 @@ impl VisitMut for ReplaceSelf<'_> { visit_mut::visit_macro_mut(self, mac); } } - -pub struct ReplaceReceiver { - pub with: Type, - pub as_trait: Option, -} - -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, 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, 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, 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![<](span), - ty: Box::new(self.self_ty(span)), - position: 0, - as_token: None, - gt_token: Token![>](span), - }); - - 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), - } - } - if modified { - *tokens = TokenStream::from_iter(out); - } - modified - } -} - -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` -> `::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` -> `::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); - } - - 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") => { - self.visit_macro_mut(&mut i.mac) - } - // Otherwise, do not recurse into nested items. - _ => {} - } - } - - fn visit_macro_mut(&mut self, mac: &mut Macro) { - // We can't tell in general whether `self` inside a macro invocation - // refers to the self in the argument list or a different self - // introduced within the macro. Heuristic: if the macro input contains - // `fn`, then `self` is more likely to refer to something other than the - // outer function's self argument. - if !contains_fn(mac.tokens.clone()) { - self.visit_token_stream(&mut mac.tokens); - } - } -} - -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(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() -} diff --git a/tests/executor/mod.rs b/tests/executor/mod.rs index f48b348..912fb79 100644 --- a/tests/executor/mod.rs +++ b/tests/executor/mod.rs @@ -4,6 +4,7 @@ use std::ptr; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; // Executor for a future that resolves immediately (test only). +#[allow(clippy::missing_panics_doc)] pub fn block_on_simple(mut fut: F) -> F::Output { unsafe fn clone(_null: *const ()) -> RawWaker { unimplemented!() From 42a0be116062f4924330aacb156a938f8523841d Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Fri, 5 Feb 2021 00:17:31 -0800 Subject: [PATCH 10/15] Gate self span hack on rustc <= 1.46. --- Cargo.toml | 3 +++ build.rs | 5 +++++ src/receiver.rs | 1 + tests/ui/self-span.stderr | 16 ++++++++-------- 4 files changed, 17 insertions(+), 8 deletions(-) create mode 100644 build.rs diff --git a/Cargo.toml b/Cargo.toml index ea0f3d4..ed186a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,5 +24,8 @@ tracing-attributes = "0.1.8" tracing-futures = "0.2" trybuild = { version = "1.0.19", features = ["diff"] } +[build-dependencies] +version_check = "0.9" + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..c2697ac --- /dev/null +++ b/build.rs @@ -0,0 +1,5 @@ +fn main() { + if let Some(true) = version_check::is_max_version("1.46") { + println!("cargo:rustc-cfg=self_span_hack"); + } +} diff --git a/src/receiver.rs b/src/receiver.rs index b497c31..b3524cd 100644 --- a/src/receiver.rs +++ b/src/receiver.rs @@ -108,6 +108,7 @@ impl VisitMut for ReplaceSelf<'_> { fn visit_ident_mut(&mut self, i: &mut Ident) { if i == "self" { *i = quote::format_ident!("{}{}", self.0, i); + #[cfg(self_span_hack)] i.set_span(self.1); } diff --git a/tests/ui/self-span.stderr b/tests/ui/self-span.stderr index bf1ca3a..9ea1bbe 100644 --- a/tests/ui/self-span.stderr +++ b/tests/ui/self-span.stderr @@ -1,10 +1,10 @@ error[E0308]: mismatched types - --> $DIR/self-span.rs:16:21 + --> $DIR/self-span.rs:17:21 | -16 | async fn method(self) { - | ^^^^ expected `()`, found struct `S` 17 | let _: () = self; - | -- expected due to this + | -- ^^^^ expected `()`, found struct `S` + | | + | expected due to this error: the `Self` constructor can only be used with tuple or unit structs --> $DIR/self-span.rs:18:23 @@ -13,12 +13,12 @@ error: the `Self` constructor can only be used with tuple or unit structs | ^^^^ help: use curly brackets: `Self { /* fields */ }` error[E0308]: mismatched types - --> $DIR/self-span.rs:24:21 + --> $DIR/self-span.rs:25:21 | -24 | async fn method(self) { - | ^^^^ expected `()`, found enum `E` 25 | let _: () = self; - | -- expected due to this + | -- ^^^^ expected `()`, found enum `E` + | | + | expected due to this error[E0533]: expected unit struct, unit variant or constant, found struct variant `Self::V` --> $DIR/self-span.rs:26:23 From c5096241ceaf22618252a8e2765defd65aff1424 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Fri, 12 Feb 2021 15:28:42 -0800 Subject: [PATCH 11/15] Add UI test for lifetime related spans. --- tests/ui/lifetime-span.rs | 21 +++++++++++++++++++++ tests/ui/lifetime-span.stderr | 27 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/ui/lifetime-span.rs create mode 100644 tests/ui/lifetime-span.stderr diff --git a/tests/ui/lifetime-span.rs b/tests/ui/lifetime-span.rs new file mode 100644 index 0000000..0326d0f --- /dev/null +++ b/tests/ui/lifetime-span.rs @@ -0,0 +1,21 @@ +use async_trait::async_trait; + +struct A; +struct B; + +#[async_trait] +pub trait Trait<'r> { + async fn method(&'r self); +} + +#[async_trait] +impl Trait for A { + async fn method(&self) { } +} + +#[async_trait] +impl<'r> Trait<'r> for B { + async fn method(&self) { } +} + +fn main() {} diff --git a/tests/ui/lifetime-span.stderr b/tests/ui/lifetime-span.stderr new file mode 100644 index 0000000..19f0c73 --- /dev/null +++ b/tests/ui/lifetime-span.stderr @@ -0,0 +1,27 @@ +error[E0726]: implicit elided lifetime not allowed here + --> $DIR/lifetime-span.rs:12:6 + | +12 | impl Trait for A { + | ^^^^^- help: indicate the anonymous lifetime: `<'_>` + +error[E0195]: lifetime parameters or bounds on method `method` do not match the trait declaration + --> $DIR/lifetime-span.rs:11:1 + | +6 | #[async_trait] + | -------------- lifetimes in impl do not match this method in trait +... +11 | #[async_trait] + | ^^^^^^^^^^^^^^ lifetimes do not match method in trait + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0195]: lifetime parameters or bounds on method `method` do not match the trait declaration + --> $DIR/lifetime-span.rs:16:1 + | +6 | #[async_trait] + | -------------- lifetimes in impl do not match this method in trait +... +16 | #[async_trait] + | ^^^^^^^^^^^^^^ lifetimes do not match method in trait + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) From 7b437f6c02c42e967cd61fc57f7b344b716a309e Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Fri, 12 Feb 2021 16:44:38 -0800 Subject: [PATCH 12/15] Properly set spans on introduced lifetimes. --- src/expand.rs | 77 +++++++++++++++++++++++++---------- src/lifetime.rs | 14 ++++--- tests/ui/lifetime-span.rs | 15 +++++++ tests/ui/lifetime-span.stderr | 45 ++++++++++++++------ 4 files changed, 111 insertions(+), 40 deletions(-) diff --git a/src/expand.rs b/src/expand.rs index 4986db0..93bd7ce 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -12,6 +12,12 @@ use syn::{ 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 { @@ -21,7 +27,7 @@ impl ToTokens for Item { } } -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] enum Context<'a> { Trait { generics: &'a Generics, @@ -46,6 +52,13 @@ impl Context<'_> { } }) } + + fn generics_span(&self) -> Span { + match self { + Context::Trait { generics, .. } => generics.span(), + Context::Impl { impl_generics } => impl_generics.span(), + } + } } type Supertraits = Punctuated; @@ -78,7 +91,7 @@ pub fn expand(input: &mut Item, is_local: bool) { } } Item::Impl(input) => { - let mut lifetimes = CollectLifetimes::new("'impl"); + let mut lifetimes = CollectLifetimes::new("'impl", input.generics.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; @@ -133,7 +146,11 @@ fn transform_sig( 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), @@ -141,13 +158,6 @@ fn transform_sig( } } - 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 @@ -157,26 +167,31 @@ fn transform_sig( 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(_) => {} } } + for elided in lifetimes.elided { - sig.generics.params.push(parse_quote!(#elided)); - where_clause + push_param(&mut sig.generics, parse_quote!(#elided)); + 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)); + + push_param(&mut sig.generics, parse_quote_spanned!(default_span => 'async_trait)); + if has_self { let bound: Ident = match sig.inputs.iter().next() { Some(FnArg::Receiver(Receiver { @@ -200,10 +215,11 @@ fn transform_sig( 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!(where_clause.span() => Self: 'async_trait) } else { - parse_quote!(Self: ::core::marker::#bound + 'async_trait) + parse_quote_spanned!(where_clause.span() => Self: ::core::marker::#bound + 'async_trait) }); } @@ -228,9 +244,9 @@ fn transform_sig( } let bounds = if is_local { - quote!('async_trait) + quote_spanned!(context.generics_span() => 'async_trait) } else { - quote!(::core::marker::Send + 'async_trait) + quote_spanned!(context.generics_span() => ::core::marker::Send + 'async_trait) }; sig.output = parse_quote! { @@ -333,3 +349,20 @@ fn has_bound(supertraits: &Supertraits, marker: &Ident) -> bool { } false } + +fn where_clause_or_default(clause: &mut Option) -> &mut WhereClause { + clause.get_or_insert_with(|| WhereClause { + where_token: Default::default(), + predicates: Punctuated::new(), + }) +} + +fn push_param(generics: &mut Generics, param: GenericParam) { + let span = param.span(); + if generics.params.is_empty() { + generics.lt_token = parse_quote_spanned!(span => <); + generics.gt_token = parse_quote_spanned!(span => >); + } + + generics.params.push(param); +} diff --git a/src/lifetime.rs b/src/lifetime.rs index 19b416f..c28c37a 100644 --- a/src/lifetime.rs +++ b/src/lifetime.rs @@ -1,4 +1,5 @@ use proc_macro2::Span; +use syn::spanned::Spanned; use syn::visit_mut::{self, VisitMut}; use syn::{GenericArgument, Lifetime, Receiver, TypeReference}; @@ -6,35 +7,38 @@ pub struct CollectLifetimes { pub elided: Vec, pub explicit: Vec, 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) { 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>>(&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/tests/ui/lifetime-span.rs b/tests/ui/lifetime-span.rs index 0326d0f..4e9e5d9 100644 --- a/tests/ui/lifetime-span.rs +++ b/tests/ui/lifetime-span.rs @@ -18,4 +18,19 @@ impl<'r> Trait<'r> for B { async fn method(&self) { } } +#[async_trait] +pub trait Trait2 { + async fn method<'r>(&'r self); +} + +#[async_trait] +impl Trait2 for A { + async fn method(&self) { } +} + +#[async_trait] +impl<'r> Trait2<'r> for B { + async fn method(&'r self) { } +} + fn main() {} diff --git a/tests/ui/lifetime-span.stderr b/tests/ui/lifetime-span.stderr index 19f0c73..feae87f 100644 --- a/tests/ui/lifetime-span.stderr +++ b/tests/ui/lifetime-span.stderr @@ -4,24 +4,43 @@ error[E0726]: implicit elided lifetime not allowed here 12 | impl Trait for A { | ^^^^^- help: indicate the anonymous lifetime: `<'_>` +error[E0107]: this trait takes 0 lifetime arguments but 1 lifetime argument was supplied + --> $DIR/lifetime-span.rs:32:10 + | +32 | impl<'r> Trait2<'r> for B { + | ^^^^^^---- help: remove these generics + | | + | expected 0 lifetime arguments + | +note: trait defined here, with 0 lifetime parameters + --> $DIR/lifetime-span.rs:22:11 + | +22 | pub trait Trait2 { + | ^^^^^^ + error[E0195]: lifetime parameters or bounds on method `method` do not match the trait declaration - --> $DIR/lifetime-span.rs:11:1 + --> $DIR/lifetime-span.rs:13:14 | -6 | #[async_trait] - | -------------- lifetimes in impl do not match this method in trait +8 | async fn method(&'r self); + | ---------------- lifetimes in impl do not match this method in trait ... -11 | #[async_trait] - | ^^^^^^^^^^^^^^ lifetimes do not match method in trait - | - = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) +13 | async fn method(&self) { } + | ^^^^^^^^^^^^^ lifetimes do not match method in trait error[E0195]: lifetime parameters or bounds on method `method` do not match the trait declaration - --> $DIR/lifetime-span.rs:16:1 + --> $DIR/lifetime-span.rs:18:14 | -6 | #[async_trait] - | -------------- lifetimes in impl do not match this method in trait +8 | async fn method(&'r self); + | ---------------- lifetimes in impl do not match this method in trait ... -16 | #[async_trait] - | ^^^^^^^^^^^^^^ lifetimes do not match method in trait +18 | async fn method(&self) { } + | ^^^^^^^^^^^^^ lifetimes do not match method in trait + +error[E0195]: lifetime parameters or bounds on method `method` do not match the trait declaration + --> $DIR/lifetime-span.rs:33:14 | - = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) +23 | async fn method<'r>(&'r self); + | ---- lifetimes in impl do not match this method in trait +... +33 | async fn method(&'r self) { } + | ^^^^^^^^^^^^^^^^ lifetimes do not match method in trait From 0cda89bd7c6ee214e4f24d29218fd450d60509ca Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Fri, 12 Feb 2021 16:46:40 -0800 Subject: [PATCH 13/15] Ignore 'clippy::type_repetition_in_bounds'. --- src/expand.rs | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/expand.rs b/src/expand.rs index 93bd7ce..beaea22 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -64,6 +64,16 @@ impl Context<'_> { type Supertraits = Punctuated; pub fn expand(input: &mut Item, is_local: bool) { + let inner_method_attrs = &[ + parse_quote!(#[allow(clippy::used_underscore_binding)]), + parse_quote!(#[allow(clippy::type_repetition_in_bounds)]), + ]; + + let trait_method_attrs = &[ + parse_quote!(#[must_use]), + parse_quote!(#[allow(clippy::type_repetition_in_bounds)]), + ]; + match input { Item::Trait(input) => { let context = Context::Trait { @@ -79,13 +89,11 @@ pub fn expand(input: &mut Item, is_local: bool) { if let Some(block) = block { has_self |= has_self_in_block(block); transform_block(sig, block); - method - .attrs - .push(parse_quote!(#[allow(clippy::used_underscore_binding)])); + method.attrs.extend_from_slice(inner_method_attrs); } let has_default = method.default.is_some(); transform_sig(context, sig, has_self, has_default, is_local); - method.attrs.push(parse_quote!(#[must_use])); + method.attrs.extend_from_slice(trait_method_attrs); } } } @@ -109,9 +117,7 @@ pub fn expand(input: &mut Item, is_local: bool) { let has_self = has_self_in_sig(sig) || has_self_in_block(block); transform_block(sig, block); transform_sig(context, sig, has_self, false, is_local); - method - .attrs - .push(parse_quote!(#[allow(clippy::used_underscore_binding)])); + method.attrs.extend_from_slice(inner_method_attrs); } } } @@ -192,13 +198,16 @@ fn transform_sig( push_param(&mut sig.generics, parse_quote_spanned!(default_span => 'async_trait)); + let first_bound = where_clause_or_default(&mut sig.generics.where_clause).predicates.first(); + let bound_span = first_bound.map_or(default_span, Spanned::span); + if has_self { let bound: Ident = match sig.inputs.iter().next() { Some(FnArg::Receiver(Receiver { reference: Some(_), mutability: None, .. - })) => parse_quote!(Sync), + })) => parse_quote_spanned!(bound_span => Sync), Some(FnArg::Typed(arg)) if match (arg.pat.as_ref(), arg.ty.as_ref()) { (Pat::Ident(pat), Type::Reference(ty)) => { @@ -207,19 +216,21 @@ fn transform_sig( _ => false, } => { - parse_quote!(Sync) + parse_quote_spanned!(bound_span => Sync) } - _ => parse_quote!(Send), + _ => parse_quote_spanned!(bound_span => Send), }; + 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_spanned!(where_clause.span() => Self: 'async_trait) + parse_quote_spanned!(bound_span => Self: 'async_trait) } else { - parse_quote_spanned!(where_clause.span() => Self: ::core::marker::#bound + 'async_trait) + parse_quote_spanned!(bound_span => Self: ::core::marker::#bound + 'async_trait) }); } From 45f9d244f36408c6f0561711b86e73d6a13b507c Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Tue, 23 Feb 2021 15:42:01 -0800 Subject: [PATCH 14/15] Remove unused 'Debug' derive for 'Context'. --- src/expand.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/expand.rs b/src/expand.rs index beaea22..05867bd 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -27,7 +27,7 @@ impl ToTokens for Item { } } -#[derive(Debug, Clone, Copy)] +#[derive(Clone, Copy)] enum Context<'a> { Trait { generics: &'a Generics, From 628ed7392a8b2a848b048ff8eeba209ad35965a0 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 24 Feb 2021 18:04:18 -0800 Subject: [PATCH 15/15] Terminate decls with 'let __async_trait: ();'. --- src/expand.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/expand.rs b/src/expand.rs index 05867bd..0b8f4a0 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -67,11 +67,13 @@ pub fn expand(input: &mut Item, is_local: bool) { let inner_method_attrs = &[ parse_quote!(#[allow(clippy::used_underscore_binding)]), parse_quote!(#[allow(clippy::type_repetition_in_bounds)]), + parse_quote!(#[allow(clippy::let_unit_value)]), ]; let trait_method_attrs = &[ parse_quote!(#[must_use]), parse_quote!(#[allow(clippy::type_repetition_in_bounds)]), + parse_quote!(#[allow(clippy::let_unit_value)]), ]; match input { @@ -335,6 +337,7 @@ fn transform_block( Box::pin(async move { let __ret: #ret_ty = { #(#decls)* + let __async_trait: (); #(#stmts)* };