Repository: dtolnay/async-trait (rust)

Issue:
Not compatible with tokio::select!.
before expand:
```rust
use async_trait::async_trait;
use std::sync::Arc;

#[async_trait]
trait MyTrait {
    async fn my_fn(self: Arc<Self>);
}

struct MyStruct {
    test_member: bool,
}

#[async_trait]
impl MyTrait for MyStruct {
    async fn my_fn(self: Arc<Self>) {
        tokio::select! {
            _ = async {
                println!("{}", self.test_member);
            } => {}
        }
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    Arc::new(MyStruct { test_member: true }).my_fn().await;

    Ok(())
}
```

after expand:
```rust
use std::sync::Arc;

trait TestTrait {
    #[must_use]
    #[allow(clippy::type_complexity, clippy::type_repetition_in_bounds)]
    fn my_fn<'async_trait>(
        self: Arc<Self>,
    ) -> ::core::pin::Pin<
        Box<dyn ::core::future::Future<Output = ()> + ::core::marker::Send + 'async_trait>,
    >
    where
        Self: 'async_trait;
}
struct TestStruct {
    test_member: bool,
}
impl TestTrait for TestStruct {
    #[allow(
        clippy::let_unit_value,
        clippy::type_complexity,
        clippy::type_repetition_in_bounds,
        clippy::used_underscore_binding
    )]
    fn my_fn<'async_trait>(
        self: Arc<Self>,
    ) -> ::core::pin::Pin<
        Box<dyn ::core::future::Future<Output = ()> + ::core::marker::Send + 'async_trait>,
    >
    where
        Self: 'async_trait,
    {
        Box::pin(async move {
            let __self = self;
            let _: () = {
                {
                    mod util {
                        pub(super) enum Out<_0> {
                            _0(_0),
                            Disabled,
                        }
                        pub(super) type Mask = u8;
                    }
                    use ::tokio::macros::support::Future;
                    use ::tokio::macros::support::Pin;
                    use ::tokio::macros::support::Poll::{Pending, Ready};
                    const BRANCHES: u32 = 1;
                    let mut disabled: util::Mask = Default::default();
                    if !true {
                        let mask = 1 << 0;
                        disabled |= mask;
                    }
                    let mut output = {
                        let mut futures = (async {
                            println!("{}", self.test_member);
                        },);
                        ::tokio::macros::support::poll_fn(|cx| {
                            let mut is_pending = false;
                            let start = { ::tokio::macros::support::thread_rng_n(BRANCHES) };
                            for i in 0..BRANCHES {
                                let branch;
                                #[allow(clippy::modulo_one)]
                                {
                                    branch = (start + i) % BRANCHES;
                                }
                                match branch {
                                    #[allow(unreachable_code)]
                                    0 => {
                                        let mask = 1 << branch;
                                        if disabled & mask == mask {
                                            continue;
                                        }
                                        let (fut, ..) = &mut futures;
                                        let mut fut = unsafe { Pin::new_unchecked(fut) };
                                        let out = match fut.poll(cx) {
                                            Ready(out) => out,
                                            Pending => {
                                                is_pending = true;
                                                continue;
                                            }
                                        };
                                        disabled |= mask;
                                        #[allow(unused_variables)]
                                        #[allow(unused_mut)]
                                        match &out {
                                            _ => {}
                                            _ => continue,
                                        }
                                        return Ready(util::Out::_0(out));
                                    }
                                    _ => {
                                        unreachable!()
                                    }
                                }
                            }
                            if is_pending {
                                Pending
                            } else {
                                Ready(util::Out::Disabled)
                            }
                        })
                        .await
                    };
                    match output {
                        util::Out::_0(_) => {}
                        util::Out::Disabled => unreachable!(),
                        _ => unreachable!(),
                    }
                }
            };
        })
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    Arc::new(TestStruct { test_member: true }).my_fn().await;

    Ok(())
}
```

![image](https://user-images.githubusercontent.com/32861476/115103747-e87ef300-9f86-11eb-8553-5f51b8343361.png)
Delete the first place, or change the `self` in the second place to `__self`.

Pull request:
Compatibility with tokio::select
Closes #161.

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index fa2b8b9..9b8611e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,6 +19,7 @@ quote = "1.0"
 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.14"
diff --git a/src/receiver.rs b/src/receiver.rs
index 7f837f4..f6ea327 100644
--- a/src/receiver.rs
+++ b/src/receiver.rs
@@ -151,9 +151,16 @@ impl VisitMut for ReplaceSelf {
 
     fn visit_item_mut(&mut self, i: &mut Item) {
         // Visit `macro_rules!` because locally defined macros can refer to
-        // `self`. Otherwise, do not recurse into nested items.
+        // `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") {
+            if i.mac.path.is_ident("macro_rules")
+                || i.mac.path.segments.last().unwrap().ident == "select"
+            {
                 self.visit_macro_mut(&mut i.mac)
             }
         }


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index 5af9f6d..6f95576 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1335,3 +1335,29 @@ pub mod issue158 {
         }
     }
 }
+
+// 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() => {}
+            }
+        }
+    }
+}