diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2022-12-13 01:27:16 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-12-13 01:27:16 +0000 |
commit | 13146ce937597d140467d00e048637fd4b487177 (patch) | |
tree | 2d6e638dc32a5d11742e35449dd2bd0d6391a05e | |
parent | 35750088b01bb18daa30fc4ae7831a975cac6c5e (diff) | |
parent | feb5d8538fadb339268c8dfdfbd65d5845416c15 (diff) | |
download | async-trait-13146ce937597d140467d00e048637fd4b487177.tar.gz |
Merge "Upgrade async-trait to 0.1.59" am: d4ef99c6cc am: feb5d8538f
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/async-trait/+/2327956
Change-Id: Ia94200c364497f643d20efdbd96de93854d991e4
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | .github/workflows/ci.yml | 35 | ||||
-rw-r--r-- | Android.bp | 2 | ||||
-rw-r--r-- | Cargo.toml | 16 | ||||
-rw-r--r-- | Cargo.toml.orig | 14 | ||||
-rw-r--r-- | METADATA | 12 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | build.rs | 4 | ||||
-rw-r--r-- | src/bound.rs | 48 | ||||
-rw-r--r-- | src/expand.rs | 186 | ||||
-rw-r--r-- | src/lib.rs | 4 | ||||
-rw-r--r-- | src/lifetime.rs | 55 | ||||
-rw-r--r-- | tests/compiletest.rs | 1 | ||||
-rw-r--r-- | tests/test.rs | 90 | ||||
-rw-r--r-- | tests/ui/arg-implementation-detail.rs | 22 | ||||
-rw-r--r-- | tests/ui/arg-implementation-detail.stderr | 5 | ||||
-rw-r--r-- | tests/ui/bare-trait-object.stderr | 14 | ||||
-rw-r--r-- | tests/ui/consider-restricting.rs | 26 | ||||
-rw-r--r-- | tests/ui/consider-restricting.stderr | 33 | ||||
-rw-r--r-- | tests/ui/delimiter-span.rs | 2 | ||||
-rw-r--r-- | tests/ui/delimiter-span.stderr | 16 | ||||
-rw-r--r-- | tests/ui/lifetime-span.rs | 8 | ||||
-rw-r--r-- | tests/ui/lifetime-span.stderr | 33 | ||||
-rw-r--r-- | tests/ui/must-use.stderr | 2 | ||||
-rw-r--r-- | tests/ui/self-span.stderr | 4 | ||||
-rw-r--r-- | tests/ui/send-not-implemented.stderr | 16 |
26 files changed, 480 insertions, 172 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 71c36c5..ed1a319 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "20bd296e0d646e2b14626b7078e045254bed26ee" + "sha1": "c1fba00e2ec717d22272d0922c6062e25181bff8" }, "path_in_vcs": "" }
\ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c80abc..531e97e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,12 @@ on: pull_request: schedule: [cron: "40 1 * * *"] +permissions: + contents: read + +env: + RUSTFLAGS: -Dwarnings + jobs: test: name: Rust ${{matrix.rust}} @@ -12,24 +18,26 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, stable, 1.45.0] + rust: [beta, stable, 1.56.0] include: - rust: nightly rustflags: --cfg async_trait_nightly_testing + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} - run: cargo test env: - RUSTFLAGS: ${{matrix.rustflags}} + RUSTFLAGS: ${{matrix.rustflags}} ${{env.RUSTFLAGS}} msrv: name: Rust 1.39.0 runs-on: ubuntu-latest + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@1.39.0 - run: cargo check @@ -37,16 +45,29 @@ jobs: name: Clippy runs-on: ubuntu-latest if: github.event_name != 'pull_request' + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@clippy - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic + miri: + name: Miri + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@miri + - run: cargo miri test + env: + MIRIFLAGS: -Zmiri-strict-provenance + outdated: name: Outdated runs-on: ubuntu-latest if: github.event_name != 'pull_request' + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/install@cargo-outdated - - run: cargo outdated --exit-code 1 + - run: cargo outdated --workspace --exit-code 1 @@ -41,7 +41,7 @@ rust_proc_macro { name: "libasync_trait", crate_name: "async_trait", cargo_env_compat: true, - cargo_pkg_version: "0.1.52", + cargo_pkg_version: "0.1.59", srcs: ["src/lib.rs"], edition: "2018", rustlibs: [ @@ -13,19 +13,25 @@ edition = "2018" rust-version = "1.39" name = "async-trait" -version = "0.1.52" +version = "0.1.59" authors = ["David Tolnay <dtolnay@gmail.com>"] description = "Type erasure for async trait methods" documentation = "https://docs.rs/async-trait" readme = "README.md" keywords = ["async"] +categories = [ + "asynchronous", + "no-std", +] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/async-trait" + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [lib] proc-macro = true + [dependencies.proc-macro2] version = "1.0" @@ -33,8 +39,12 @@ version = "1.0" version = "1.0" [dependencies.syn] -version = "1.0.61" -features = ["full", "visit-mut"] +version = "1.0.96" +features = [ + "full", + "visit-mut", +] + [dev-dependencies.futures] version = "0.3" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index c340ced..090eef9 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,15 +1,15 @@ [package] name = "async-trait" -version = "0.1.52" +version = "0.1.59" authors = ["David Tolnay <dtolnay@gmail.com>"] -edition = "2018" -rust-version = "1.39" -license = "MIT OR Apache-2.0" +categories = ["asynchronous", "no-std"] description = "Type erasure for async trait methods" -repository = "https://github.com/dtolnay/async-trait" documentation = "https://docs.rs/async-trait" -readme = "README.md" +edition = "2018" keywords = ["async"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/dtolnay/async-trait" +rust-version = "1.39" [lib] proc-macro = true @@ -17,7 +17,7 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" -syn = { version = "1.0.61", features = ["full", "visit-mut"] } +syn = { version = "1.0.96", features = ["full", "visit-mut"] } [dev-dependencies] futures = "0.3" @@ -1,3 +1,7 @@ +# This project was upgraded with external_updater. +# Usage: tools/external_updater/updater.sh update rust/crates/async-trait +# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md + name: "async-trait" description: "Type erasure for async trait methods" third_party { @@ -7,13 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/async-trait/async-trait-0.1.52.crate" + value: "https://static.crates.io/crates/async-trait/async-trait-0.1.59.crate" } - version: "0.1.52" + version: "0.1.59" license_type: NOTICE last_upgrade_date { year: 2022 - month: 3 - day: 1 + month: 12 + day: 6 } } @@ -3,7 +3,7 @@ Async trait methods [<img alt="github" src="https://img.shields.io/badge/github-dtolnay/async--trait-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/async-trait) [<img alt="crates.io" src="https://img.shields.io/crates/v/async-trait.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/async-trait) -[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-async--trait-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/async-trait) +[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-async--trait-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/async-trait) [<img alt="build status" src="https://img.shields.io/github/workflow/status/dtolnay/async-trait/CI/master?style=for-the-badge" height="20">](https://github.com/dtolnay/async-trait/actions?query=branch%3Amaster) The initial round of stabilizations for the async/await language feature in Rust @@ -8,6 +8,10 @@ fn main() { None => return, }; + if compiler < 45 { + println!("cargo:rustc-cfg=no_span_mixed_site"); + } + if compiler < 47 { println!("cargo:rustc-cfg=self_span_hack"); } diff --git a/src/bound.rs b/src/bound.rs new file mode 100644 index 0000000..50182f6 --- /dev/null +++ b/src/bound.rs @@ -0,0 +1,48 @@ +use proc_macro2::{Ident, Span, TokenStream}; +use quote::quote_spanned; +use syn::punctuated::Punctuated; +use syn::{Token, TypeParamBound}; + +pub type Supertraits = Punctuated<TypeParamBound, Token![+]>; + +pub enum InferredBound { + Send, + Sync, +} + +pub fn has_bound(supertraits: &Supertraits, bound: &InferredBound) -> bool { + for supertrait in supertraits { + if let TypeParamBound::Trait(supertrait) = supertrait { + if supertrait.path.is_ident(bound) + || supertrait.path.segments.len() == 3 + && (supertrait.path.segments[0].ident == "std" + || supertrait.path.segments[0].ident == "core") + && supertrait.path.segments[1].ident == "marker" + && supertrait.path.segments[2].ident == *bound + { + return true; + } + } + } + false +} + +impl InferredBound { + fn as_str(&self) -> &str { + match self { + InferredBound::Send => "Send", + InferredBound::Sync => "Sync", + } + } + + pub fn spanned_path(&self, span: Span) -> TokenStream { + let ident = Ident::new(self.as_str(), span); + quote_spanned!(span=> ::core::marker::#ident) + } +} + +impl PartialEq<InferredBound> for Ident { + fn eq(&self, bound: &InferredBound) -> bool { + self == bound.as_str() + } +} diff --git a/src/expand.rs b/src/expand.rs index 2f4697a..53918cb 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -1,23 +1,19 @@ -use crate::lifetime::CollectLifetimes; +use crate::bound::{has_bound, InferredBound, Supertraits}; +use crate::lifetime::{AddLifetimeToImplTrait, CollectLifetimes}; use crate::parse::Item; use crate::receiver::{has_self_in_block, has_self_in_sig, mut_pat, ReplaceSelf}; -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::collections::BTreeSet as Set; +use std::mem; use syn::punctuated::Punctuated; use syn::visit_mut::{self, VisitMut}; use syn::{ - parse_quote, Attribute, Block, FnArg, GenericParam, Generics, Ident, ImplItem, Lifetime, Pat, - PatIdent, Receiver, ReturnType, Signature, Stmt, Token, TraitItem, Type, TypeParamBound, - TypePath, WhereClause, + parse_quote, parse_quote_spanned, Attribute, Block, FnArg, GenericArgument, GenericParam, + Generics, Ident, ImplItem, Lifetime, LifetimeDef, Pat, PatIdent, PathArguments, Receiver, + ReturnType, Signature, Stmt, Token, TraitItem, Type, 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 { @@ -40,23 +36,22 @@ enum Context<'a> { } impl Context<'_> { - fn lifetimes<'a>(&'a self, used: &'a [Lifetime]) -> impl Iterator<Item = &'a GenericParam> { + fn lifetimes<'a>(&'a self, used: &'a [Lifetime]) -> impl Iterator<Item = &'a LifetimeDef> { let generics = match self { Context::Trait { generics, .. } => generics, Context::Impl { impl_generics, .. } => impl_generics, }; - generics.params.iter().filter(move |param| { + generics.params.iter().filter_map(move |param| { if let GenericParam::Lifetime(param) = param { - used.contains(¶m.lifetime) - } else { - false + if used.contains(¶m.lifetime) { + return Some(param); + } } + None }) } } -type Supertraits = Punctuated<TypeParamBound, Token![+]>; - pub fn expand(input: &mut Item, is_local: bool) { match input { Item::Trait(input) => { @@ -163,19 +158,14 @@ fn transform_sig( has_default: bool, is_local: bool, ) { - sig.fn_token.span = sig.asyncness.take().unwrap().span; + let default_span = sig.asyncness.take().unwrap().span; + sig.fn_token.span = default_span; - let ret = match &sig.output { - ReturnType::Default => quote!(()), - ReturnType::Type(_, ret) => quote!(#ret), + let (ret_arrow, ret) = match &sig.output { + ReturnType::Default => (Token![->](default_span), quote_spanned!(default_span=> ())), + ReturnType::Type(arrow, ret) => (*arrow, quote!(#ret)), }; - 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 { @@ -184,31 +174,42 @@ fn transform_sig( } } - for param in sig - .generics - .params - .iter() - .chain(context.lifetimes(&lifetimes.explicit)) - { + for param in &mut sig.generics.params { match param { GenericParam::Type(param) => { - let param = ¶m.ident; - let span = param.span(); + let param_name = ¶m.ident; + let span = match param.colon_token.take() { + Some(colon_token) => colon_token.span, + None => param_name.span(), + }; + let bounds = mem::replace(&mut param.bounds, Punctuated::new()); where_clause_or_default(&mut sig.generics.where_clause) .predicates - .push(parse_quote_spanned!(span=> #param: 'async_trait)); + .push(parse_quote_spanned!(span=> #param_name: 'async_trait + #bounds)); } GenericParam::Lifetime(param) => { - let param = ¶m.lifetime; - let span = param.span(); + let param_name = ¶m.lifetime; + let span = match param.colon_token.take() { + Some(colon_token) => colon_token.span, + None => param_name.span(), + }; + let bounds = mem::replace(&mut param.bounds, Punctuated::new()); where_clause_or_default(&mut sig.generics.where_clause) .predicates - .push(parse_quote_spanned!(span=> #param: 'async_trait)); + .push(parse_quote_spanned!(span=> #param: 'async_trait + #bounds)); } GenericParam::Const(_) => {} } } + for param in context.lifetimes(&lifetimes.explicit) { + let param = ¶m.lifetime; + let span = param.span(); + where_clause_or_default(&mut sig.generics.where_clause) + .predicates + .push(parse_quote_spanned!(span=> #param: 'async_trait)); + } + if sig.generics.lt_token.is_none() { sig.generics.lt_token = Some(Token![<](sig.ident.span())); } @@ -228,37 +229,65 @@ fn transform_sig( .push(parse_quote_spanned!(default_span=> 'async_trait)); if has_self { - let bound_span = sig.ident.span(); - let bound = match sig.inputs.iter().next() { + let bounds: &[InferredBound] = match sig.inputs.iter().next() { Some(FnArg::Receiver(Receiver { reference: Some(_), mutability: None, .. - })) => Ident::new("Sync", bound_span), + })) => &[InferredBound::Sync], Some(FnArg::Typed(arg)) - if match (arg.pat.as_ref(), arg.ty.as_ref()) { - (Pat::Ident(pat), Type::Reference(ty)) => { - pat.ident == "self" && ty.mutability.is_none() - } + if match arg.pat.as_ref() { + Pat::Ident(pat) => pat.ident == "self", _ => false, } => { - Ident::new("Sync", bound_span) + match arg.ty.as_ref() { + // self: &Self + Type::Reference(ty) if ty.mutability.is_none() => &[InferredBound::Sync], + // self: Arc<Self> + Type::Path(ty) + if { + let segment = ty.path.segments.last().unwrap(); + segment.ident == "Arc" + && match &segment.arguments { + PathArguments::AngleBracketed(arguments) => { + arguments.args.len() == 1 + && match &arguments.args[0] { + GenericArgument::Type(Type::Path(arg)) => { + arg.path.is_ident("Self") + } + _ => false, + } + } + _ => false, + } + } => + { + &[InferredBound::Sync, InferredBound::Send] + } + _ => &[InferredBound::Send], + } } - _ => Ident::new("Send", bound_span), + _ => &[InferredBound::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!(bound_span=> Self: 'async_trait) - } else { - parse_quote_spanned!(bound_span=> Self: ::core::marker::#bound + 'async_trait) + let bounds = bounds.iter().filter_map(|bound| { + let assume_bound = match context { + Context::Trait { supertraits, .. } => !has_default || has_bound(supertraits, bound), + Context::Impl { .. } => true, + }; + if assume_bound || is_local { + None + } else { + Some(bound.spanned_path(default_span)) + } }); + + where_clause_or_default(&mut sig.generics.where_clause) + .predicates + .push(parse_quote_spanned! {default_span=> + Self: #(#bounds +)* 'async_trait + }); } for (i, arg) in sig.inputs.iter_mut().enumerate() { @@ -276,18 +305,18 @@ fn transform_sig( let m = mut_pat(&mut arg.pat); arg.pat = parse_quote!(#m #positional); } + AddLifetimeToImplTrait.visit_type_mut(&mut arg.ty); } } } - let ret_span = sig.ident.span(); let bounds = if is_local { - quote_spanned!(ret_span=> 'async_trait) + quote_spanned!(default_span=> 'async_trait) } else { - quote_spanned!(ret_span=> ::core::marker::Send + 'async_trait) + quote_spanned!(default_span=> ::core::marker::Send + 'async_trait) }; - sig.output = parse_quote_spanned! {ret_span=> - -> ::core::pin::Pin<Box< + sig.output = parse_quote_spanned! {default_span=> + #ret_arrow ::core::pin::Pin<Box< dyn ::core::future::Future<Output = #ret> + #bounds >> }; @@ -347,7 +376,11 @@ fn transform_block(context: Context, sig: &mut Signature, block: &mut Block) { } else { let pat = &arg.pat; let ident = positional_arg(i, pat); - quote!(let #pat = #ident;) + if let Pat::Wild(_) = **pat { + quote!(let #ident = #ident;) + } else { + quote!(let #pat = #ident;) + } } } }) @@ -391,25 +424,10 @@ fn transform_block(context: Context, sig: &mut Signature, block: &mut Block) { } 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 { - for bound in supertraits { - if let TypeParamBound::Trait(bound) = bound { - if bound.path.is_ident(marker) - || bound.path.segments.len() == 3 - && (bound.path.segments[0].ident == "std" - || bound.path.segments[0].ident == "core") - && bound.path.segments[1].ident == "marker" - && bound.path.segments[2].ident == *marker - { - return true; - } - } - } - false + let span: Span = syn::spanned::Spanned::span(pat); + #[cfg(not(no_span_mixed_site))] + let span = span.resolved_at(Span::mixed_site()); + format_ident!("__arg{}", i, span = span) } fn contains_associated_type_impl_trait(context: Context, ret: &mut Type) -> bool { @@ -2,7 +2,7 @@ //! //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust -//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K +//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs //! //! <br> //! @@ -306,6 +306,7 @@ #![allow( clippy::default_trait_access, clippy::doc_markdown, + clippy::explicit_auto_deref, clippy::if_not_else, clippy::items_after_statements, clippy::module_name_repetitions, @@ -317,6 +318,7 @@ extern crate proc_macro; mod args; +mod bound; mod expand; mod lifetime; mod parse; diff --git a/src/lifetime.rs b/src/lifetime.rs index ff25d32..8e4ec38 100644 --- a/src/lifetime.rs +++ b/src/lifetime.rs @@ -1,6 +1,10 @@ -use proc_macro2::Span; +use proc_macro2::{Span, TokenStream}; +use std::mem; use syn::visit_mut::{self, VisitMut}; -use syn::{GenericArgument, Lifetime, Receiver, TypeReference}; +use syn::{ + parse_quote_spanned, token, Expr, GenericArgument, Lifetime, Receiver, ReturnType, Type, + TypeBareFn, TypeImplTrait, TypeParen, TypePtr, TypeReference, +}; pub struct CollectLifetimes { pub elided: Vec<Lifetime>, @@ -62,3 +66,50 @@ impl VisitMut for CollectLifetimes { visit_mut::visit_generic_argument_mut(self, gen); } } + +pub struct AddLifetimeToImplTrait; + +impl VisitMut for AddLifetimeToImplTrait { + fn visit_type_impl_trait_mut(&mut self, ty: &mut TypeImplTrait) { + let span = ty.impl_token.span; + let lifetime = parse_quote_spanned!(span=> 'async_trait); + ty.bounds.insert(0, lifetime); + if let Some(punct) = ty.bounds.pairs_mut().next().unwrap().punct_mut() { + punct.span = span; + } + visit_mut::visit_type_impl_trait_mut(self, ty); + } + + fn visit_type_reference_mut(&mut self, ty: &mut TypeReference) { + parenthesize_impl_trait(&mut ty.elem, ty.and_token.span); + visit_mut::visit_type_reference_mut(self, ty); + } + + fn visit_type_ptr_mut(&mut self, ty: &mut TypePtr) { + parenthesize_impl_trait(&mut ty.elem, ty.star_token.span); + visit_mut::visit_type_ptr_mut(self, ty); + } + + fn visit_type_bare_fn_mut(&mut self, ty: &mut TypeBareFn) { + if let ReturnType::Type(arrow, return_type) = &mut ty.output { + parenthesize_impl_trait(return_type, arrow.spans[0]); + } + visit_mut::visit_type_bare_fn_mut(self, ty); + } + + fn visit_expr_mut(&mut self, _e: &mut Expr) { + // Do not recurse into impl Traits inside of an array length expression. + // + // fn outer(arg: [u8; { fn inner(_: impl Trait) {}; 0 }]); + } +} + +fn parenthesize_impl_trait(elem: &mut Type, paren_span: Span) { + if let Type::ImplTrait(_) = *elem { + let placeholder = Type::Verbatim(TokenStream::new()); + *elem = Type::Paren(TypeParen { + paren_token: token::Paren(paren_span), + elem: Box::new(mem::replace(elem, placeholder)), + }); + } +} diff --git a/tests/compiletest.rs b/tests/compiletest.rs index f9aea23..7974a62 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -1,4 +1,5 @@ #[rustversion::attr(not(nightly), ignore)] +#[cfg_attr(miri, ignore)] #[test] fn ui() { let t = trybuild::TestCases::new(); diff --git a/tests/test.rs b/tests/test.rs index 2bca1fc..23d8f80 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -3,7 +3,6 @@ feature(min_specialization, type_alias_impl_trait) )] #![allow( - clippy::let_underscore_drop, clippy::let_unit_value, clippy::missing_panics_doc, clippy::missing_safety_doc, @@ -618,6 +617,7 @@ pub mod issue45 { } #[test] + #[cfg_attr(miri, ignore)] // https://github.com/matklad/once_cell/pull/185 fn tracing() { // Create the future outside of the subscriber, as no call to tracing // should be made until the future is polled. @@ -913,7 +913,7 @@ pub mod issue92 { const ASSOCIATED2: &'static str; type Associated2; - #[allow(path_statements, clippy::no_effect)] + #[allow(path_statements, clippy::let_underscore_future, clippy::no_effect)] async fn associated2(&self) { // trait items mac!(let _: Self::Associated2;); @@ -936,7 +936,7 @@ pub mod issue92 { const ASSOCIATED2: &'static str = "2"; type Associated2 = (); - #[allow(path_statements, clippy::no_effect)] + #[allow(path_statements, clippy::let_underscore_future, clippy::no_effect)] async fn associated2(&self) { // inherent items mac!(Self::ASSOCIATED1;); @@ -1038,9 +1038,9 @@ pub mod issue106 { } #[async_trait] - impl<P: ?Sized> ProcessPool for &P + impl<P> ProcessPool for &P where - P: ProcessPool, + P: ?Sized + ProcessPool, { type ThreadPool = P::ThreadPool; @@ -1056,8 +1056,6 @@ pub mod issue106 { // https://github.com/dtolnay/async-trait/issues/110 pub mod issue110 { - #![deny(clippy::all)] - use async_trait::async_trait; use std::marker::PhantomData; @@ -1113,8 +1111,6 @@ pub mod issue123 { // https://github.com/dtolnay/async-trait/issues/129 pub mod issue129 { - #![deny(clippy::pedantic)] - use async_trait::async_trait; #[async_trait] @@ -1378,6 +1374,23 @@ pub mod issue169 { pub fn test(_t: &dyn Trait) {} } +// https://github.com/dtolnay/async-trait/issues/177 +pub mod issue177 { + use async_trait::async_trait; + + #[async_trait] + pub trait Trait { + async fn foo(&self, _callback: impl FnMut(&str) + Send) {} + } + + pub struct Struct; + + #[async_trait] + impl Trait for Struct { + async fn foo(&self, _callback: impl FnMut(&str) + Send) {} + } +} + // https://github.com/dtolnay/async-trait/issues/183 pub mod issue183 { #![deny(clippy::shadow_same)] @@ -1389,3 +1402,62 @@ pub mod issue183 { async fn foo(_n: i32) {} } } + +// https://github.com/dtolnay/async-trait/issues/199 +pub mod issue199 { + use async_trait::async_trait; + use std::cell::Cell; + + struct IncrementOnDrop<'a>(&'a Cell<usize>); + + impl<'a> Drop for IncrementOnDrop<'a> { + fn drop(&mut self) { + self.0.set(self.0.get() + 1); + } + } + + #[async_trait(?Send)] + trait Trait { + async fn f(counter: &Cell<usize>, arg: IncrementOnDrop<'_>); + } + + struct Struct; + + #[async_trait(?Send)] + impl Trait for Struct { + async fn f(counter: &Cell<usize>, _: IncrementOnDrop<'_>) { + assert_eq!(counter.get(), 0); // second arg not dropped yet + } + } + + #[test] + fn test() { + let counter = Cell::new(0); + let future = Struct::f(&counter, IncrementOnDrop(&counter)); + assert_eq!(counter.get(), 0); + drop(future); + assert_eq!(counter.get(), 1); + } +} + +// https://github.com/dtolnay/async-trait/issues/204 +pub mod issue204 { + use async_trait::async_trait; + + #[async_trait] + pub trait Trait { + async fn f(arg: &impl Trait); + async fn g(arg: *const impl Trait); + } +} + +// https://github.com/dtolnay/async-trait/issues/210 +pub mod issue210 { + use async_trait::async_trait; + use std::sync::Arc; + + #[async_trait] + pub trait Trait { + async fn f(self: Arc<Self>) {} + } +} diff --git a/tests/ui/arg-implementation-detail.rs b/tests/ui/arg-implementation-detail.rs new file mode 100644 index 0000000..b83aa72 --- /dev/null +++ b/tests/ui/arg-implementation-detail.rs @@ -0,0 +1,22 @@ +use async_trait::async_trait; + +pub struct Struct; + +#[async_trait] +pub trait Trait { + async fn f((_a, _b): (Struct, Struct)) { + // Expands to something like: + // + // fn f(__arg0: (Struct, Struct)) -> … { + // Box::pin(async move { + // let (_a, _b) = __arg0; + // … + // }) + // } + // + // but user's code must not be allowed to name that temporary argument: + let _ = __arg0; + } +} + +fn main() {} diff --git a/tests/ui/arg-implementation-detail.stderr b/tests/ui/arg-implementation-detail.stderr new file mode 100644 index 0000000..e742688 --- /dev/null +++ b/tests/ui/arg-implementation-detail.stderr @@ -0,0 +1,5 @@ +error[E0425]: cannot find value `__arg0` in this scope + --> tests/ui/arg-implementation-detail.rs:18:17 + | +18 | let _ = __arg0; + | ^^^^^^ not found in this scope diff --git a/tests/ui/bare-trait-object.stderr b/tests/ui/bare-trait-object.stderr index 6670c48..50b2048 100644 --- a/tests/ui/bare-trait-object.stderr +++ b/tests/ui/bare-trait-object.stderr @@ -2,12 +2,20 @@ error: trait objects without an explicit `dyn` are deprecated --> tests/ui/bare-trait-object.rs:11:16 | 11 | impl Trait for Send + Sync { - | ^^^^^^^^^^^ help: use `dyn`: `dyn Send + Sync` + | ^^^^^^^^^^^ | + = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2021! + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> note: the lint level is defined here --> tests/ui/bare-trait-object.rs:1:9 | 1 | #![deny(bare_trait_objects)] | ^^^^^^^^^^^^^^^^^^ - = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2021! - = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> +help: use `dyn` + | +11 | impl Trait for dyn Send + Sync { + | +++ +help: alternatively use a blanket implementation to implement `Trait` for all types that also implement `Send + Sync` + | +11 | impl<T: Send + Sync> Trait for T { + | ++++++++++++++++ ~ diff --git a/tests/ui/consider-restricting.rs b/tests/ui/consider-restricting.rs new file mode 100644 index 0000000..e23c8b1 --- /dev/null +++ b/tests/ui/consider-restricting.rs @@ -0,0 +1,26 @@ +// https://github.com/rust-lang/rust/issues/93828 + +use async_trait::async_trait; + +pub trait IntoUrl {} + +#[async_trait] +pub trait ClientExt { + async fn publish<T: IntoUrl>(&self, url: T); +} + +struct Client; + +#[async_trait] +impl ClientExt for Client { + async fn publish<T: IntoUrl>(&self, url: T) {} +} + +struct Client2; + +#[async_trait] +impl ClientExt for Client2 { + async fn publish<T>(&self, url: T) {} +} + +fn main() {} diff --git a/tests/ui/consider-restricting.stderr b/tests/ui/consider-restricting.stderr new file mode 100644 index 0000000..62ff894 --- /dev/null +++ b/tests/ui/consider-restricting.stderr @@ -0,0 +1,33 @@ +error: future cannot be sent between threads safely + --> tests/ui/consider-restricting.rs:16:49 + | +16 | async fn publish<T: IntoUrl>(&self, url: T) {} + | ^^ future created by async block is not `Send` + | +note: captured value is not `Send` + --> tests/ui/consider-restricting.rs:16:41 + | +16 | async fn publish<T: IntoUrl>(&self, url: T) {} + | ^^^ has type `T` which is not `Send` + = note: required for the cast from `[async block@$DIR/tests/ui/consider-restricting.rs:16:49: 16:51]` to the object type `dyn Future<Output = ()> + Send` +help: consider further restricting this bound + | +16 | async fn publish<T: IntoUrl + std::marker::Send>(&self, url: T) {} + | +++++++++++++++++++ + +error: future cannot be sent between threads safely + --> tests/ui/consider-restricting.rs:23:40 + | +23 | async fn publish<T>(&self, url: T) {} + | ^^ future created by async block is not `Send` + | +note: captured value is not `Send` + --> tests/ui/consider-restricting.rs:23:32 + | +23 | async fn publish<T>(&self, url: T) {} + | ^^^ has type `T` which is not `Send` + = note: required for the cast from `[async block@$DIR/tests/ui/consider-restricting.rs:23:40: 23:42]` to the object type `dyn Future<Output = ()> + Send` +help: consider further restricting this bound + | +23 | async fn publish<T + std::marker::Send>(&self, url: T) {} + | +++++++++++++++++++ diff --git a/tests/ui/delimiter-span.rs b/tests/ui/delimiter-span.rs index d3f67a1..51a44a2 100644 --- a/tests/ui/delimiter-span.rs +++ b/tests/ui/delimiter-span.rs @@ -1,3 +1,5 @@ +#![allow(unused_macro_rules)] + use async_trait::async_trait; macro_rules! picky { diff --git a/tests/ui/delimiter-span.stderr b/tests/ui/delimiter-span.stderr index a13985d..f03da4c 100644 --- a/tests/ui/delimiter-span.stderr +++ b/tests/ui/delimiter-span.stderr @@ -1,17 +1,21 @@ error: no rules expected the token `{` - --> tests/ui/delimiter-span.rs:17:16 + --> tests/ui/delimiter-span.rs:19:16 | -3 | macro_rules! picky { +5 | macro_rules! picky { | ------------------ when calling this macro ... -17 | picky!({ 123, self }); +19 | picky!({ 123, self }); | ^ no rules expected this token in macro call + | + = note: while trying to match sequence start error: no rules expected the token `{` - --> tests/ui/delimiter-span.rs:18:16 + --> tests/ui/delimiter-span.rs:20:16 | -3 | macro_rules! picky { +5 | macro_rules! picky { | ------------------ when calling this macro ... -18 | picky!({ 123 }); +20 | picky!({ 123 }); | ^ no rules expected this token in macro call + | + = note: while trying to match sequence start diff --git a/tests/ui/lifetime-span.rs b/tests/ui/lifetime-span.rs index 4e9e5d9..01981e6 100644 --- a/tests/ui/lifetime-span.rs +++ b/tests/ui/lifetime-span.rs @@ -10,12 +10,12 @@ pub trait Trait<'r> { #[async_trait] impl Trait for A { - async fn method(&self) { } + async fn method(&self) {} } #[async_trait] impl<'r> Trait<'r> for B { - async fn method(&self) { } + async fn method(&self) {} } #[async_trait] @@ -25,12 +25,12 @@ pub trait Trait2 { #[async_trait] impl Trait2 for A { - async fn method(&self) { } + async fn method(&self) {} } #[async_trait] impl<'r> Trait2<'r> for B { - async fn method(&'r self) { } + async fn method(&'r self) {} } fn main() {} diff --git a/tests/ui/lifetime-span.stderr b/tests/ui/lifetime-span.stderr index aad25a7..999da04 100644 --- a/tests/ui/lifetime-span.stderr +++ b/tests/ui/lifetime-span.stderr @@ -2,9 +2,13 @@ error[E0726]: implicit elided lifetime not allowed here --> tests/ui/lifetime-span.rs:12:6 | 12 | impl Trait for A { - | ^^^^^- help: indicate the anonymous lifetime: `<'_>` + | ^^^^^ expected lifetime parameter | = note: assuming a `'static` lifetime... +help: indicate the anonymous lifetime + | +12 | impl Trait<'_> for A { + | ++++ error[E0107]: this trait takes 0 lifetime arguments but 1 lifetime argument was supplied --> tests/ui/lifetime-span.rs:32:10 @@ -19,30 +23,3 @@ note: trait defined here, with 0 lifetime parameters | 22 | pub trait Trait2 { | ^^^^^^ - -error[E0195]: lifetime parameters or bounds on method `method` do not match the trait declaration - --> tests/ui/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 - --> tests/ui/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 - --> tests/ui/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/must-use.stderr b/tests/ui/must-use.stderr index 1b97055..fd6fc31 100644 --- a/tests/ui/must-use.stderr +++ b/tests/ui/must-use.stderr @@ -2,7 +2,7 @@ error: unused return value of `Interface::f` that must be used --> tests/ui/must-use.rs:18:5 | 18 | Thing.f(); - | ^^^^^^^^^^ + | ^^^^^^^^^ | note: the lint level is defined here --> tests/ui/must-use.rs:1:9 diff --git a/tests/ui/self-span.stderr b/tests/ui/self-span.stderr index 2690791..8743e57 100644 --- a/tests/ui/self-span.stderr +++ b/tests/ui/self-span.stderr @@ -20,8 +20,8 @@ error[E0308]: mismatched types | | | expected due to this -error[E0533]: expected unit struct, unit variant or constant, found struct variant `Self::V` +error[E0533]: expected value, found struct variant `Self::V` --> tests/ui/self-span.rs:26:23 | 26 | let _: Self = Self::V; - | ^^^^^^^ + | ^^^^^^^ not a value diff --git a/tests/ui/send-not-implemented.stderr b/tests/ui/send-not-implemented.stderr index 8004de6..d68fc43 100644 --- a/tests/ui/send-not-implemented.stderr +++ b/tests/ui/send-not-implemented.stderr @@ -9,17 +9,17 @@ error: future cannot be sent between threads safely 12 | | } | |_____^ future created by async block is not `Send` | - = help: within `impl Future<Output = [async output]>`, the trait `Send` is not implemented for `MutexGuard<'_, ()>` + = help: within `[async block@$DIR/tests/ui/send-not-implemented.rs:8:26: 12:6]`, the trait `Send` is not implemented for `MutexGuard<'_, ()>` note: future is not `Send` as this value is used across an await - --> tests/ui/send-not-implemented.rs:11:9 + --> tests/ui/send-not-implemented.rs:11:12 | 10 | let _guard = mutex.lock().unwrap(); | ------ has type `MutexGuard<'_, ()>` which is not `Send` 11 | f().await; - | ^^^^^^^^^ await occurs here, with `_guard` maybe used later + | ^^^^^^ await occurs here, with `_guard` maybe used later 12 | } | - `_guard` is later dropped here - = note: required for the cast to the object type `dyn Future<Output = ()> + Send` + = note: required for the cast from `[async block@$DIR/tests/ui/send-not-implemented.rs:8:26: 12:6]` to the object type `dyn Future<Output = ()> + Send` error: future cannot be sent between threads safely --> tests/ui/send-not-implemented.rs:14:38 @@ -33,15 +33,15 @@ error: future cannot be sent between threads safely 19 | | } | |_____^ future created by async block is not `Send` | - = help: within `impl Future<Output = [async output]>`, the trait `Send` is not implemented for `MutexGuard<'_, ()>` + = help: within `[async block@$DIR/tests/ui/send-not-implemented.rs:14:38: 19:6]`, the trait `Send` is not implemented for `MutexGuard<'_, ()>` note: future is not `Send` as this value is used across an await - --> tests/ui/send-not-implemented.rs:17:9 + --> tests/ui/send-not-implemented.rs:17:12 | 16 | let _guard = mutex.lock().unwrap(); | ------ has type `MutexGuard<'_, ()>` which is not `Send` 17 | f().await; - | ^^^^^^^^^ await occurs here, with `_guard` maybe used later + | ^^^^^^ 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` + = note: required for the cast from `[async block@$DIR/tests/ui/send-not-implemented.rs:14:38: 19:6]` to the object type `dyn Future<Output = bool> + Send` |