aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2021-04-22 01:05:08 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2021-04-22 01:05:08 +0000
commit7541456ba74c5a1974d0fd0d47c93524f3835243 (patch)
tree9739f4bea3e54934e235716a483b610f81d2504b
parentb9d66b8faf2a323b8529ae99a26320da825305ba (diff)
parentaf5acc173f260c1ebf4a2a25b0da83b86ffeef7f (diff)
downloadasync-trait-android12-s3-release.tar.gz
Snap for 7302914 from af5acc173f260c1ebf4a2a25b0da83b86ffeef7f to sc-releaseandroid-vts-12.0_r9android-vts-12.0_r8android-vts-12.0_r7android-vts-12.0_r6android-vts-12.0_r5android-vts-12.0_r4android-vts-12.0_r3android-vts-12.0_r2android-vts-12.0_r12android-vts-12.0_r11android-vts-12.0_r10android-vts-12.0_r1android-security-12.0.0_r59android-security-12.0.0_r58android-security-12.0.0_r57android-security-12.0.0_r56android-security-12.0.0_r55android-security-12.0.0_r54android-security-12.0.0_r53android-security-12.0.0_r52android-security-12.0.0_r51android-security-12.0.0_r50android-security-12.0.0_r49android-security-12.0.0_r48android-security-12.0.0_r47android-security-12.0.0_r46android-security-12.0.0_r45android-security-12.0.0_r44android-security-12.0.0_r43android-security-12.0.0_r42android-security-12.0.0_r41android-security-12.0.0_r40android-security-12.0.0_r39android-security-12.0.0_r38android-security-12.0.0_r37android-security-12.0.0_r36android-security-12.0.0_r35android-security-12.0.0_r34android-security-11.0.0_r71android-platform-12.0.0_r9android-platform-12.0.0_r8android-platform-12.0.0_r7android-platform-12.0.0_r6android-platform-12.0.0_r5android-platform-12.0.0_r4android-platform-12.0.0_r31android-platform-12.0.0_r30android-platform-12.0.0_r3android-platform-12.0.0_r29android-platform-12.0.0_r28android-platform-12.0.0_r27android-platform-12.0.0_r26android-platform-12.0.0_r25android-platform-12.0.0_r24android-platform-12.0.0_r23android-platform-12.0.0_r22android-platform-12.0.0_r21android-platform-12.0.0_r20android-platform-12.0.0_r2android-platform-12.0.0_r19android-platform-12.0.0_r18android-platform-12.0.0_r17android-platform-12.0.0_r16android-platform-12.0.0_r15android-platform-12.0.0_r14android-platform-12.0.0_r13android-platform-12.0.0_r12android-platform-12.0.0_r11android-platform-12.0.0_r10android-platform-12.0.0_r1android-cts-12.0_r9android-cts-12.0_r8android-cts-12.0_r7android-cts-12.0_r6android-cts-12.0_r5android-cts-12.0_r4android-cts-12.0_r3android-cts-12.0_r2android-cts-12.0_r12android-cts-12.0_r11android-cts-12.0_r10android-cts-12.0_r1android-12.0.0_r9android-12.0.0_r8android-12.0.0_r34android-12.0.0_r33android-12.0.0_r31android-12.0.0_r30android-12.0.0_r3android-12.0.0_r25android-12.0.0_r2android-12.0.0_r11android-12.0.0_r10android-12.0.0_r1android12-tests-releaseandroid12-security-releaseandroid12-s5-releaseandroid12-s4-releaseandroid12-s3-releaseandroid12-s2-releaseandroid12-s1-releaseandroid12-releaseandroid12-platform-release
Change-Id: I544bf7560faa52985347a1cb982c8a220041e5ef
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--.clippy.toml1
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--Android.bp7
-rw-r--r--Cargo.toml13
-rw-r--r--Cargo.toml.orig9
-rw-r--r--METADATA10
-rw-r--r--TEST_MAPPING8
-rw-r--r--build.rs25
-rw-r--r--src/expand.rs487
-rw-r--r--src/lib.rs12
-rw-r--r--src/lifetime.rs34
-rw-r--r--src/receiver.rs310
-rw-r--r--src/respan.rs22
-rw-r--r--tests/executor/mod.rs1
-rw-r--r--tests/test.rs286
-rw-r--r--tests/ui/delimiter-span.rs3
-rw-r--r--tests/ui/delimiter-span.stderr11
-rw-r--r--tests/ui/lifetime-span.rs36
-rw-r--r--tests/ui/lifetime-span.stderr46
-rw-r--r--tests/ui/self-span.stderr15
-rw-r--r--tests/ui/send-not-implemented.rs7
-rw-r--r--tests/ui/send-not-implemented.stderr27
-rw-r--r--tests/ui/unreachable.rs20
-rw-r--r--tests/ui/unreachable.stderr14
-rw-r--r--tests/ui/unsupported-self.stderr2
26 files changed, 830 insertions, 580 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 181672b..d7f5c7b 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
{
"git": {
- "sha1": "f54e5f2a4ad7fad8700b1c809d6c96893388b30d"
+ "sha1": "651ddc1131325b08c1b76ae6b65c1f23ca4cf7cf"
}
}
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
diff --git a/Android.bp b/Android.bp
index 935757e..22a10c5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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.69 "clone-impls,default,derive,extra-traits,full,parsing,printing,proc-macro,quote,visit,visit-mut"
// unicode-xid-0.2.1 "default"
diff --git a/Cargo.toml b/Cargo.toml
index 0f21963..e97aafa 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,11 +13,12 @@
[package]
edition = "2018"
name = "async-trait"
-version = "0.1.42"
+version = "0.1.50"
authors = ["David Tolnay <dtolnay@gmail.com>"]
description = "Type erasure for async trait methods"
documentation = "https://docs.rs/async-trait"
readme = "README.md"
+keywords = ["async"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/dtolnay/async-trait"
[package.metadata.docs.rs]
@@ -32,8 +33,11 @@ version = "1.0"
version = "1.0"
[dependencies.syn]
-version = "1.0"
+version = "1.0.61"
features = ["full", "visit-mut"]
+[dev-dependencies.futures]
+version = "0.3"
+
[dev-dependencies.rustversion]
version = "1.0"
@@ -41,10 +45,7 @@ version = "1.0"
version = "0.1.14"
[dev-dependencies.tracing-attributes]
-version = "0.1.8"
-
-[dev-dependencies.tracing-futures]
-version = "0.2"
+version = "0.1.14"
[dev-dependencies.trybuild]
version = "1.0.19"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index ea0f3d4..8af0b3b 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "async-trait"
-version = "0.1.42"
+version = "0.1.50"
authors = ["David Tolnay <dtolnay@gmail.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
@@ -8,6 +8,7 @@ description = "Type erasure for async trait methods"
repository = "https://github.com/dtolnay/async-trait"
documentation = "https://docs.rs/async-trait"
readme = "README.md"
+keywords = ["async"]
[lib]
proc-macro = true
@@ -15,13 +16,13 @@ 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]
+futures = "0.3"
rustversion = "1.0"
tracing = "0.1.14"
-tracing-attributes = "0.1.8"
-tracing-futures = "0.2"
+tracing-attributes = "0.1.14"
trybuild = { version = "1.0.19", features = ["diff"] }
[package.metadata.docs.rs]
diff --git a/METADATA b/METADATA
index 8d7bc4f..4f74759 100644
--- a/METADATA
+++ b/METADATA
@@ -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.50.crate"
}
- version: "0.1.42"
+ version: "0.1.50"
license_type: NOTICE
last_upgrade_date {
- year: 2020
- month: 11
- day: 24
+ year: 2021
+ month: 4
+ day: 21
}
}
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..0f4f93c
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,8 @@
+// Generated by update_crate_tests.py for tests that depend on this crate.
+{
+ "presubmit": [
+ {
+ "name": "authfs_device_test_src_lib"
+ }
+ ]
+}
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 = &param.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 = &param.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(),
+ })
+}
diff --git a/src/lib.rs b/src/lib.rs
index 929af4f..100bee6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -303,7 +303,16 @@
//! let object = &value as &dyn ObjectSafe;
//! ```
-#![allow(clippy::match_like_matches_macro)] // matches! requires Rust 1.42
+#![allow(
+ clippy::default_trait_access,
+ clippy::doc_markdown,
+ clippy::if_not_else,
+ clippy::items_after_statements,
+ clippy::module_name_repetitions,
+ clippy::shadow_unrelated,
+ clippy::similar_names,
+ clippy::too_many_lines
+)]
extern crate proc_macro;
@@ -312,7 +321,6 @@ mod 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..f6ea327 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, Path, 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,84 @@ 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);
- }
+impl VisitMut for ReplaceSelf {
+ fn visit_ident_mut(&mut self, i: &mut Ident) {
+ self.prepend_underscore_to_self(i);
}
- // `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);
+ fn visit_path_mut(&mut self, p: &mut Path) {
+ if p.segments.len() == 1 {
+ // Replace `self`, but not `self::function`.
+ self.visit_ident_mut(&mut p.segments[0].ident);
}
- 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);
+ for segment in &mut p.segments {
+ self.visit_path_arguments_mut(&mut segment.arguments);
}
- 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") => {
+ // Visit `macro_rules!` because locally defined macros can refer to
+ // `self`.
+ //
+ // Visit `futures::select` and similar select macros, which commonly
+ // appear syntactically like an item despite expanding to an expression.
+ //
+ // Otherwise, do not recurse into nested items.
+ if let Item::Macro(i) = i {
+ if i.mac.path.is_ident("macro_rules")
+ || i.mac.path.segments.last().unwrap().ident == "select"
+ {
self.visit_macro_mut(&mut i.mac)
}
- // Otherwise, do not recurse into nested items.
- _ => {}
}
}
@@ -303,19 +177,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..6f95576 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_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;
@@ -1077,3 +1156,208 @@ 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);
+ }
+ }
+}
+
+// https://github.com/dtolnay/async-trait/issues/158
+pub mod issue158 {
+ use async_trait::async_trait;
+
+ fn f() {}
+
+ #[async_trait]
+ pub trait Trait {
+ async fn f(&self) {
+ self::f()
+ }
+ }
+}
+
+// https://github.com/dtolnay/async-trait/issues/161
+#[allow(clippy::mut_mut)]
+pub mod issue161 {
+ use async_trait::async_trait;
+ use futures::future::FutureExt;
+ use std::sync::Arc;
+
+ #[async_trait]
+ pub trait Trait {
+ async fn f(self: Arc<Self>);
+ }
+
+ pub struct MyStruct(bool);
+
+ #[async_trait]
+ impl Trait for MyStruct {
+ async fn f(self: Arc<Self>) {
+ futures::select! {
+ _ = async {
+ println!("{}", self.0);
+ }.fuse() => {}
+ }
+ }
+ }
+}
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;