diff options
author | Joel Galenson <jgalenson@google.com> | 2021-04-01 15:10:30 -0700 |
---|---|---|
committer | Joel Galenson <jgalenson@google.com> | 2021-04-01 15:10:30 -0700 |
commit | 5448f37621f113b92e2dd4c77626bf73476490b4 (patch) | |
tree | 4b260ee9ad47bd37b004537f4c3baedcc2611631 | |
parent | bc03a9763251939fe06ff1fa7f784294efc5511c (diff) | |
download | async-trait-5448f37621f113b92e2dd4c77626bf73476490b4.tar.gz |
Upgrade rust/crates/async-trait to 0.1.48
Test: make
Change-Id: I898f35cd086b1bd9952eba11d5d59d31f78a5d16
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | .clippy.toml | 1 | ||||
-rw-r--r-- | .github/workflows/ci.yml | 2 | ||||
-rw-r--r-- | Android.bp | 7 | ||||
-rw-r--r-- | Cargo.toml | 4 | ||||
-rw-r--r-- | Cargo.toml.orig | 4 | ||||
-rw-r--r-- | METADATA | 10 | ||||
-rw-r--r-- | build.rs | 25 | ||||
-rw-r--r-- | src/expand.rs | 487 | ||||
-rw-r--r-- | src/lib.rs | 12 | ||||
-rw-r--r-- | src/lifetime.rs | 34 | ||||
-rw-r--r-- | src/receiver.rs | 301 | ||||
-rw-r--r-- | src/respan.rs | 22 | ||||
-rw-r--r-- | tests/executor/mod.rs | 1 | ||||
-rw-r--r-- | tests/test.rs | 247 | ||||
-rw-r--r-- | tests/ui/delimiter-span.rs | 3 | ||||
-rw-r--r-- | tests/ui/delimiter-span.stderr | 11 | ||||
-rw-r--r-- | tests/ui/lifetime-span.rs | 36 | ||||
-rw-r--r-- | tests/ui/lifetime-span.stderr | 46 | ||||
-rw-r--r-- | tests/ui/self-span.stderr | 15 | ||||
-rw-r--r-- | tests/ui/send-not-implemented.rs | 7 | ||||
-rw-r--r-- | tests/ui/send-not-implemented.stderr | 27 | ||||
-rw-r--r-- | tests/ui/unreachable.rs | 20 | ||||
-rw-r--r-- | tests/ui/unreachable.stderr | 14 | ||||
-rw-r--r-- | tests/ui/unsupported-self.stderr | 2 |
25 files changed, 762 insertions, 578 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 181672b..a638e3f 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,5 @@ { "git": { - "sha1": "f54e5f2a4ad7fad8700b1c809d6c96893388b30d" + "sha1": "a4aab7b9285e4912d16f1f4733cfb99fc8aa37da" } } diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 0000000..21a08b0 --- /dev/null +++ b/.clippy.toml @@ -0,0 +1 @@ +msrv = "1.39.0" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 710cee3..4c3d289 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,4 +39,4 @@ jobs: steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@clippy - - run: cargo clippy --tests -- -Dclippy::all + - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic @@ -1,4 +1,5 @@ // This file is generated by cargo2android.py --run --device --dependencies. +// Do not modify this file as changes will be overridden on upgrade. package { default_applicable_licenses: ["external_rust_crates_async-trait_license"], @@ -49,7 +50,7 @@ rust_proc_macro { } // dependent_library ["feature_list"] -// proc-macro2-1.0.24 "default,proc-macro" -// quote-1.0.7 "default,proc-macro" -// syn-1.0.51 "clone-impls,default,derive,extra-traits,full,parsing,printing,proc-macro,quote,visit,visit-mut" +// proc-macro2-1.0.26 "default,proc-macro" +// quote-1.0.9 "default,proc-macro" +// syn-1.0.68 "clone-impls,default,derive,extra-traits,full,parsing,printing,proc-macro,quote,visit,visit-mut" // unicode-xid-0.2.1 "default" @@ -13,7 +13,7 @@ [package] edition = "2018" name = "async-trait" -version = "0.1.42" +version = "0.1.48" authors = ["David Tolnay <dtolnay@gmail.com>"] description = "Type erasure for async trait methods" documentation = "https://docs.rs/async-trait" @@ -32,7 +32,7 @@ version = "1.0" version = "1.0" [dependencies.syn] -version = "1.0" +version = "1.0.61" features = ["full", "visit-mut"] [dev-dependencies.rustversion] version = "1.0" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index ea0f3d4..d5599b1 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,6 @@ [package] name = "async-trait" -version = "0.1.42" +version = "0.1.48" authors = ["David Tolnay <dtolnay@gmail.com>"] edition = "2018" license = "MIT OR Apache-2.0" @@ -15,7 +15,7 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" -syn = { version = "1.0", features = ["full", "visit-mut"] } +syn = { version = "1.0.61", features = ["full", "visit-mut"] } [dev-dependencies] rustversion = "1.0" @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/async-trait/async-trait-0.1.42.crate" + value: "https://static.crates.io/crates/async-trait/async-trait-0.1.48.crate" } - version: "0.1.42" + version: "0.1.48" license_type: NOTICE last_upgrade_date { - year: 2020 - month: 11 - day: 24 + year: 2021 + month: 4 + day: 1 } } diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..a2b0833 --- /dev/null +++ b/build.rs @@ -0,0 +1,25 @@ +use std::env; +use std::process::Command; +use std::str; + +fn main() { + let compiler = match rustc_minor_version() { + Some(compiler) => compiler, + None => return, + }; + + if compiler < 47 { + println!("cargo:rustc-cfg=self_span_hack"); + } +} + +fn rustc_minor_version() -> Option<u32> { + let rustc = env::var_os("RUSTC")?; + let output = Command::new(rustc).arg("--version").output().ok()?; + let version = str::from_utf8(&output.stdout).ok()?; + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + pieces.next()?.parse().ok() +} 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 @@ 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, + associated_type_impl_traits: &'a Set<Ident>, }, } @@ -59,7 +61,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, }; @@ -69,32 +70,40 @@ pub fn expand(input: &mut Item, is_local: bool) { 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 @@ 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(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 @@ pub fn expand(input: &mut Item, is_local: bool) { } } +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 @@ 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), @@ -149,13 +182,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 @@ -165,33 +191,48 @@ 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(_) => {} } } + + if sig.generics.lt_token.is_none() { + sig.generics.lt_token = Some(Token![<](sig.ident.span())); + } + if sig.generics.gt_token.is_none() { + sig.generics.gt_token = Some(Token![>](sig.paren_token.span)); + } + 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 @@ fn transform_sig( _ => 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 @@ fn transform_sig( 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 @@ fn transform_sig( } // 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); - } + } else { + let pat = &arg.pat; + let ident = positional_arg(i, pat); + quote!(let #pat = #ident;) + } + } + }) + .collect::<Vec<_>>(); - if has_async_lifetime(&mut standalone, block) { - standalone.generics.params.push(parse_quote!('async_trait)); + if let Some(span) = self_span { + let mut replace_self = ReplaceSelf(span); + replace_self.visit_block_mut(block); } - let mut types = standalone - .generics - .type_params() - .map(|param| param.ident.clone()) - .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 - }; + 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 @@ fn has_bound(supertraits: &Supertraits, marker: &Ident) -> bool { } 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(), + }) +} @@ -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 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..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 @@ 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); @@ -37,6 +26,32 @@ fn has_self_in_token_stream(tokens: TokenStream) -> bool { }) } +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 @@ impl VisitMut for HasSelf { } } -pub struct ReplaceReceiver { - pub with: Type, - pub as_trait: Option<Path>, -} - -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![<](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; - } +pub struct ReplaceSelf(pub Span); - 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>); +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 { + *ident = Ident::new("__self", ident.span()); + #[cfg(self_span_hack)] + ident.set_span(self.0); } + modified } - fn visit_token_stream(&self, tokens: &mut TokenStream) -> bool { + fn visit_token_stream(&mut 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 { + 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), } - 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` -> `<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 @@ 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 { - *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() -} 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<F: Future>(mut fut: F) -> F::Output { unsafe fn clone(_null: *const ()) -> RawWaker { unimplemented!() diff --git a/tests/test.rs b/tests/test.rs index 5fc238b..604d092 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,6 +1,12 @@ #![cfg_attr( async_trait_nightly_testing, - feature(min_specialization, min_const_generics) + feature(min_specialization, min_const_generics, type_alias_impl_trait) +)] +#![allow( + clippy::let_underscore_drop, + clippy::let_unit_value, + clippy::missing_panics_doc, + clippy::trivially_copy_pass_by_ref )] use async_trait::async_trait; @@ -151,6 +157,79 @@ 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; + } + } +} + +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); + } + } +} + +pub async fn test_inference() { + #[async_trait] + pub trait Trait { + async fn f() -> Box<dyn Iterator<Item = ()>> { + Box::new(std::iter::empty()) + } + } +} + +pub async fn test_internal_items() { + #[async_trait] + #[allow(dead_code, clippy::items_after_statements)] + pub trait Trait: Sized { + async fn f(self) { + struct Struct; + + impl Struct { + fn f(self) { + let _ = self; + } + } + } + } +} + +pub async fn test_unimplemented() { + #[async_trait] + pub trait Trait { + async fn f() { + unimplemented!() + } + } +} + // https://github.com/dtolnay/async-trait/issues/1 pub mod issue1 { use async_trait::async_trait; @@ -536,6 +615,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. @@ -1077,3 +1157,168 @@ pub mod issue134 { } } } + +// https://github.com/dtolnay/async-trait/pull/125#pullrequestreview-491880881 +pub mod drop_order { + use crate::executor; + use async_trait::async_trait; + use std::sync::atomic::{AtomicBool, Ordering}; + + 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)); + } +} + +// https://github.com/dtolnay/async-trait/issues/145 +pub mod issue145 { + #![deny(clippy::type_complexity)] + + use async_trait::async_trait; + + #[async_trait] + pub trait ManageConnection: Sized + Send + Sync + 'static { + type Connection: Send + 'static; + type Error: Send + 'static; + + async fn connect(&self) -> Result<Self::Connection, Self::Error>; + } +} + +// https://github.com/dtolnay/async-trait/issues/147 +pub mod issue147 { + #![deny(clippy::let_unit_value)] + + use async_trait::async_trait; + + pub struct MyType; + + #[async_trait] + pub trait MyTrait { + async fn x(); + async fn y() -> (); + async fn z(); + } + + #[async_trait] + impl MyTrait for MyType { + async fn x() {} + async fn y() -> () {} + async fn z() { + unimplemented!() + } + } +} + +// https://github.com/dtolnay/async-trait/issues/149 +pub mod issue149 { + use async_trait::async_trait; + + pub struct Thing; + pub trait Ret {} + impl Ret for Thing {} + + pub async fn ok() -> &'static dyn Ret { + return &Thing; + } + + #[async_trait] + pub trait Trait { + async fn fail() -> &'static dyn Ret { + return &Thing; + } + } +} + +// https://github.com/dtolnay/async-trait/issues/152 +#[cfg(async_trait_nightly_testing)] +pub mod issue152 { + use async_trait::async_trait; + + #[async_trait] + trait Trait { + type Assoc; + + async fn f(&self) -> Self::Assoc; + } + + struct Struct; + + #[async_trait] + impl Trait for Struct { + type Assoc = impl Sized; + + async fn f(&self) -> Self::Assoc {} + } +} + +// https://github.com/dtolnay/async-trait/issues/154 +pub mod issue154 { + #![deny(clippy::items_after_statements)] + + use async_trait::async_trait; + + #[async_trait] + pub trait MyTrait { + async fn f(&self); + } + + pub struct Struct; + + #[async_trait] + impl MyTrait for Struct { + async fn f(&self) { + const MAX: u16 = 128; + println!("{}", MAX); + } + } +} 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 diff --git a/tests/ui/lifetime-span.rs b/tests/ui/lifetime-span.rs new file mode 100644 index 0000000..4e9e5d9 --- /dev/null +++ b/tests/ui/lifetime-span.rs @@ -0,0 +1,36 @@ +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) { } +} + +#[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 new file mode 100644 index 0000000..feae87f --- /dev/null +++ b/tests/ui/lifetime-span.stderr @@ -0,0 +1,46 @@ +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[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:13:14 + | +8 | async fn method(&'r self); + | ---------------- lifetimes in impl do not match this method in trait +... +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:18:14 + | +8 | async fn method(&'r self); + | ---------------- lifetimes in impl do not match this 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 + | +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 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.rs b/tests/ui/send-not-implemented.rs index a3e3856..d8883fb 100644 --- a/tests/ui/send-not-implemented.rs +++ b/tests/ui/send-not-implemented.rs @@ -10,6 +10,13 @@ trait Test { let _guard = mutex.lock().unwrap(); f().await; } + + async fn test_ret(&self) -> bool { + let mutex = Mutex::new(()); + let _guard = mutex.lock().unwrap(); + f().await; + true + } } fn main() {} diff --git a/tests/ui/send-not-implemented.stderr b/tests/ui/send-not-implemented.stderr index 05c445b..473a31b 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 @@ -20,3 +20,28 @@ note: future is not `Send` as this value is used across an await 12 | } | - `_guard` is later dropped here = note: required for the cast to the object type `dyn Future<Output = ()> + Send` + +error: future cannot be sent between threads safely + --> $DIR/send-not-implemented.rs:14:38 + | +14 | async fn test_ret(&self) -> bool { + | ______________________________________^ +15 | | let mutex = Mutex::new(()); +16 | | let _guard = mutex.lock().unwrap(); +17 | | f().await; +18 | | true +19 | | } + | |_____^ 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 + --> $DIR/send-not-implemented.rs:17:9 + | +16 | let _guard = mutex.lock().unwrap(); + | ------ has type `MutexGuard<'_, ()>` which is not `Send` +17 | f().await; + | ^^^^^^^^^ await occurs here, with `_guard` maybe used later +18 | true +19 | } + | - `_guard` is later dropped here + = note: required for the cast to the object type `dyn Future<Output = bool> + Send` 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)]` 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; |