Repository: dtolnay/async-trait (rust)

Issue:
Self keyword inside macro invocations in trait impl
Issue #73 only revealed part of the problem, and only part of it was fixed.

Since Merge Request #74 `Self` inside macro invocation works when used in the trait declaration, but the issue persists in the trait impl. See this example:

```rust
use async_trait::async_trait;

struct Foo;

#[async_trait]
pub trait Example {
    const ASSOCIATED: &'static str;

    async fn associated(&self) {
        println!("Associated:{}", Self::ASSOCIATED); // <-- This works now
    }

    async fn associated2(&self);
}

#[async_trait]
impl Example for Foo {
    const ASSOCIATED: &'static str = "foo";

    async fn associated2(&self) {
        println!("Associated:{}", Self::ASSOCIATED); // <-- This does not
    }
}
```
Compiler output:
```
   Compiling foo v0.1.0 (/tmp/foo)
error[E0433]: failed to resolve: use of undeclared type or module `AsyncTrait`
  --> src/lib.rs:21:35
   |
21 |         println!("Associated:{}", Self::ASSOCIATED);
   |                                   ^^^^ use of undeclared type or module `AsyncTrait`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0433`.
error: could not compile `foo`.

To learn more, run the command again with --verbose.
```

Tested with 0.1.30.

Workaround from original issue (`let`-binding) also works.

Pull request:
Fix handling of Self keyword inside macro invocations
Fixes #92

Patch:
diff --git a/src/receiver.rs b/src/receiver.rs
index 1a36e7a..b08e73a 100644
--- a/src/receiver.rs
+++ b/src/receiver.rs
@@ -1,4 +1,5 @@
 use proc_macro2::{Group, TokenStream, TokenTree};
+use quote::quote;
 use std::iter::FromIterator;
 use std::mem;
 use syn::punctuated::Punctuated;
@@ -153,6 +154,55 @@ impl ReplaceReceiver {
             *path = parse_quote!(::core::marker::PhantomData::<#error>);
         }
     }
+
+    fn visit_token_stream(&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.with;
+                            match iter.peek() {
+                                Some(TokenTree::Punct(p)) if p.as_char() == ':' => {
+                                    let next = iter.next().unwrap();
+                                    match iter.peek() {
+                                        Some(TokenTree::Punct(p)) if p.as_char() == ':' => {
+                                            out.extend(quote!(<#self_ty>))
+                                        }
+                                        _ => out.extend(quote!(#self_ty)),
+                                    }
+                                    out.push(next);
+                                }
+                                _ => out.extend(quote!(#self_ty)),
+                            }
+                        }
+                    } else {
+                        out.push(TokenTree::Ident(ident));
+                    }
+                }
+                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 {
@@ -226,7 +276,7 @@ impl VisitMut for ReplaceReceiver {
         // `fn`, then `self` is more likely to refer to something other than the
         // outer function's self argument.
         if !contains_fn(i.tokens.clone()) {
-            fold_token_stream(&mut i.tokens);
+            self.visit_token_stream(&mut i.tokens);
         }
     }
 }
@@ -239,32 +289,6 @@ fn contains_fn(tokens: TokenStream) -> bool {
     })
 }
 
-fn fold_token_stream(tokens: &mut TokenStream) -> bool {
-    let mut out = Vec::new();
-    let mut modified = false;
-    for tt in tokens.clone() {
-        match tt {
-            TokenTree::Ident(mut ident) => {
-                modified |= prepend_underscore_to_self(&mut ident);
-                modified |= replace_self_from_outer_function(&mut ident);
-                out.push(TokenTree::Ident(ident));
-            }
-            TokenTree::Group(group) => {
-                let mut content = group.stream();
-                modified |= fold_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
-}
-
 fn prepend_underscore_to_self(ident: &mut Ident) -> bool {
     let modified = ident == "self";
     if modified {
@@ -272,11 +296,3 @@ fn prepend_underscore_to_self(ident: &mut Ident) -> bool {
     }
     modified
 }
-
-fn replace_self_from_outer_function(ident: &mut Ident) -> bool {
-    let modified = ident == "Self";
-    if modified {
-        *ident = Ident::new("AsyncTrait", ident.span());
-    }
-    modified
-}


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index 3a42629..4bca199 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -785,3 +785,85 @@ pub mod issue89 {
         async fn f(&self) {}
     }
 }
+
+// https://github.com/dtolnay/async-trait/issues/92
+pub mod issue92 {
+    use async_trait::async_trait;
+
+    macro_rules! mac {
+        ($($tt:tt)*) => {
+            $($tt)*
+        };
+    }
+
+    pub struct Struct<T> {
+        _x: T,
+    }
+
+    impl<T> Struct<T> {
+        const ASSOCIATED1: &'static str = "1";
+        async fn associated1() {}
+    }
+
+    #[async_trait]
+    pub trait Trait
+    where
+        mac!(Self): Send,
+    {
+        const ASSOCIATED2: &'static str;
+        type Associated2;
+
+        #[allow(path_statements)]
+        async fn associated2(&self) {
+            // trait items
+            mac!(let _: Self::Associated2;);
+            mac!(let _: <Self>::Associated2;);
+            mac!(let _: <Self as Trait>::Associated2;);
+            mac!(Self::ASSOCIATED2;);
+            mac!(<Self>::ASSOCIATED2;);
+            mac!(<Self as Trait>::ASSOCIATED2;);
+            mac!(let _ = Self::associated2(self););
+            mac!(let _ = <Self>::associated2(self););
+            mac!(let _ = <Self as Trait>::associated2(self););
+        }
+    }
+
+    #[async_trait]
+    impl<T: Send + Sync> Trait for Struct<T>
+    where
+        mac!(Self): Send,
+    {
+        const ASSOCIATED2: &'static str = "2";
+        type Associated2 = ();
+
+        #[allow(path_statements)]
+        async fn associated2(&self) {
+            // inherent items
+            mac!(Self::ASSOCIATED1;);
+            mac!(<Self>::ASSOCIATED1;);
+            mac!(let _ = Self::associated1(););
+            mac!(let _ = <Self>::associated1(););
+
+            // trait items
+            mac!(let _: <Self as Trait>::Associated2;);
+            mac!(Self::ASSOCIATED2;);
+            mac!(<Self>::ASSOCIATED2;);
+            mac!(<Self as Trait>::ASSOCIATED2;);
+            mac!(let _ = Self::associated2(self););
+            mac!(let _ = <Self>::associated2(self););
+            mac!(let _ = <Self as Trait>::associated2(self););
+        }
+    }
+
+    pub struct Unit;
+
+    #[async_trait]
+    impl Trait for Unit {
+        const ASSOCIATED2: &'static str = "2";
+        type Associated2 = ();
+
+        async fn associated2(&self) {
+            mac!(let Self: Self = *self;);
+        }
+    }
+}