Repository: dtolnay/async-trait (Rust)

Issue:
`self` in locally defined macro doesn't get rewritten
Hi there and thanks for the great lib!

I just noticed that `self`, when referenced from a locally defined macro, is not correctly rewritten to `_self`.

```rust
#[async_trait::async_trait]
trait Magic {
    async fn foo(&self);
}

struct Bar {
    x: i32,
}

#[async_trait::async_trait]
impl Magic for Bar {
    async fn foo(&self) {
        macro_rules! ref_self {
            () => {
                // reference self somehow
                let _x = self.x;
            }
        }

        ref_self!();
    }
}

fn main() {}
```

expands to

```rust
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std;
trait Magic {
    fn foo<'life0, 'async_trait>(
        &'life0 self,
    ) -> ::core::pin::Pin<
        Box<dyn ::core::future::Future<Output = ()> + ::core::marker::Send + 'async_trait>,
    >
    where
        'life0: 'async_trait,
        Self: 'async_trait;
}
struct Bar {
    x: i32,
}
impl Magic for Bar {
    fn foo<'life0, 'async_trait>(
        &'life0 self,
    ) -> ::core::pin::Pin<
        Box<dyn ::core::future::Future<Output = ()> + ::core::marker::Send + 'async_trait>,
    >
    where
        'life0: 'async_trait,
        Self: 'async_trait,
    {
        #[allow(
            clippy::missing_docs_in_private_items,
            clippy::type_repetition_in_bounds,
            clippy::used_underscore_binding
        )]
        async fn __foo(_self: &Bar) {
            let _x = self.x;
        }
        Box::pin(__foo(self))
    }
}
fn main() {}
```

resulting in

```
error[E0434]: can't capture dynamic environment in a fn item
  --> src/main.rs:16:26
   |
16 |                 let _x = self.x;
   |                          ^^^^
...
20 |         ref_self!();
   |         ------------ in this macro invocation
   |
   = help: use the `|| { ... }` closure form instead
```

Currently, I just work around this by just passing it in as `$self:ident`.


Discussion:
I just updated to the latest version but this is still a problem for me in the case where `macro_rules` defines a trait implementation that uses `async_trait`.

The following code fails to compile:

```rust
fn main() {
    let _ = Bar.foo();
}

#[async_trait::async_trait]
trait Foo {
    async fn foo(&self);
}

macro_rules! impl_foo {
    ($target:ty) => {
        #[async_trait::async_trait]
        impl Foo for $target {
            async fn foo(&self) {

            }
        }
    };
}

struct Bar;

impl_foo!(Bar);
```

It expands to (using `cargo expand`):

```rust
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std;
fn main() {
    let _ = Bar.foo();
}
trait Foo {
    #[must_use]
    fn foo<'life0, 'async_trait>(
        &'life0 self,
    ) -> ::core::pin::Pin<
        Box<dyn ::core::future::Future<Output = ()> + ::core::marker::Send + 'async_trait>,
    >
        where
            'life0: 'async_trait,
            Self: 'async_trait;
}
struct Bar;
impl Foo for Bar {
    fn foo<'life0, 'async_trait>(
        &'life0 self,
    ) -> ::core::pin::Pin<
        Box<dyn ::core::future::Future<Output = ()> + ::core::marker::Send + 'async_trait>,
    >
        where
            'life0: 'async_trait,
            Self: 'async_trait,
    {
        #[allow(
        clippy::missing_docs_in_private_items,
        clippy::type_repetition_in_bounds,
        clippy::used_underscore_binding
        )]
        async fn __foo(_self: &Bar) {}
        Box::pin(__foo(self))
    }
}
```

Interestingly this compiles if you remove the prelude import stuff :thinking: 
Is there a different between what `cargo expand` and what rustc does?
That one is a rustc bug: https://github.com/dtolnay/async-trait/issues/46 / https://github.com/rust-lang/rust/issues/43081.
You should be able to work around it by using `$target:tt` or `$($target:tt)*` instead of `$target:ty`.



Patch:
diff --git a/src/receiver.rs b/src/receiver.rs
index bb7a134..6eaa53a 100644
--- a/src/receiver.rs
+++ b/src/receiver.rs
@@ -4,8 +4,8 @@ use std::mem;
 use syn::punctuated::Punctuated;
 use syn::visit_mut::{self, VisitMut};
 use syn::{
-    parse_quote, Block, Error, ExprPath, ExprStruct, Ident, Item, Macro, Path, PathArguments,
-    QSelf, Receiver, Signature, Type, TypePath, WherePredicate,
+    parse_quote, Block, Error, ExprPath, ExprStruct, Ident, Item, ItemMacro, Macro, Path,
+    PathArguments, QSelf, Receiver, Signature, Type, TypePath, WherePredicate,
 };
 
 pub fn has_self_in_sig(sig: &mut Signature) -> bool {
@@ -174,8 +174,15 @@ impl VisitMut for ReplaceReceiver {
         visit_mut::visit_expr_struct_mut(self, expr);
     }
 
-    fn visit_item_mut(&mut self, _: &mut Item) {
-        // Do not recurse into nested items.
+    fn visit_item_mut(&mut self, i: &mut Item) {
+        match i {
+            // Visit `macro_rules!` because locally defined macros can refer to `self`.
+            Item::Macro(ItemMacro { mac, .. }) if mac.path.is_ident("macro_rules") => {
+                self.visit_macro_mut(mac)
+            }
+            // Otherwise, do not recurse into nested items.
+            _ => {}
+        }
     }
 
     fn visit_macro_mut(&mut self, i: &mut Macro) {


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index d4fca0e..d17b051 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -516,3 +516,19 @@ pub mod issue73 {
         }
     }
 }
+
+// https://github.com/dtolnay/async-trait/issues/68
+pub mod issue68 {
+    #[rustversion::since(1.40)] // procedural macros cannot expand to macro definitions in 1.39.
+    #[async_trait::async_trait]
+    pub trait Example {
+        async fn method(&self) {
+            macro_rules! t {
+                () => {{
+                    let _: &Self = self;
+                }};
+            }
+            t!();
+        }
+    }
+}