Repository: dtolnay/async-trait (rust)

Issue:
Attributes on impl parameters are not forwarded to body
Heya, I was implementing a trait function with `#[cfg(..)]` in its parameters, and encountered the following error:

```rust
// within trait impl
async fn cfg_param(
    &self,
    #[cfg(not(feature = "something"))] param: u8,
    #[cfg(feature = "something")] _unused: u8,
    //                            ^^^^^^^
    // cannot find value `_unused` in this scope
) -> u8 {
    #[cfg(not(feature = "something"))]
    { param }
    #[cfg(feature = "something")]
    { 1 }
}
```

The expanded code body shows:

```rust
let __self = self;
let param = param;
let _unused = _unused;
let __ret: u8 = { #[cfg(not(feature = "something"))] { param } };
#[allow(unreachable_code)] __ret
```

Since `_unused` is not present in the function signature, the expanded code should likely just omit it.

#227 forwards the attributes, such that the expanded code returns:

```rust
let __self = self;
#[cfg(not(feature = "something"))]
let param = param;
let __ret: u8 = { #[cfg(not(feature = "something"))] { param } };
#[allow(unreachable_code)] __ret
```

The `_unused` statement isn't present since it's been removed by the expansion.

Pull request:
Forward attributes on `impl` function parameters to variable declaration.
Fixes #226.

It feels rare to have attributes on parameters, I can't imagine a concrete scenario where forwarding all attributes is undesirable.
We could filter the attributes that are forwarded to only the `#[cfg(..)]` ones.

Patch:
diff --git a/src/expand.rs b/src/expand.rs
index 53918cb..8e6cd28 100644
--- a/src/expand.rs
+++ b/src/expand.rs
@@ -362,6 +362,12 @@ fn transform_block(context: Context, sig: &mut Signature, block: &mut Block) {
                 quote!(let #mutability #ident = #self_token;)
             }
             FnArg::Typed(arg) => {
+                // If there is a `#[cfg(..)]` attribute that selectively
+                // enables the parameter, forward it to the variable.
+                //
+                // This is currently not applied to the `self` parameter
+                let attrs = arg.attrs.iter();
+
                 if let Pat::Ident(PatIdent {
                     ident, mutability, ..
                 }) = &*arg.pat
@@ -371,15 +377,24 @@ fn transform_block(context: Context, sig: &mut Signature, block: &mut Block) {
                         let prefixed = Ident::new("__self", ident.span());
                         quote!(let #mutability #prefixed = #ident;)
                     } else {
-                        quote!(let #mutability #ident = #ident;)
+                        quote!(
+                            #(#attrs)*
+                            let #mutability #ident = #ident;
+                        )
                     }
                 } else {
                     let pat = &arg.pat;
                     let ident = positional_arg(i, pat);
                     if let Pat::Wild(_) = **pat {
-                        quote!(let #ident = #ident;)
+                        quote!(
+                            #(#attrs)*
+                            let #ident = #ident;
+                        )
                     } else {
-                        quote!(let #pat = #ident;)
+                        quote!(
+                            #(#attrs)*
+                            let #pat = #ident;
+                        )
                     }
                 }
             }


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index 9dcc673..7b9078d 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -54,6 +54,10 @@ trait Trait {
     async fn calls_mut(&mut self) {
         self.selfmut().await;
     }
+
+    async fn cfg_param(&self, param: u8);
+    async fn cfg_param_wildcard(&self, _: u8);
+    async fn cfg_param_tuple(&self, (left, right): (u8, u8));
 }
 
 struct Struct;
@@ -87,6 +91,17 @@ impl Trait for Struct {
     async fn calls_mut(&mut self) {
         self.selfmut().await;
     }
+
+    async fn cfg_param(&self, #[cfg(any())] param: u8, #[cfg(all())] _unused: u8) {}
+
+    async fn cfg_param_wildcard(&self, #[cfg(any())] _: u8, #[cfg(all())] _: u8) {}
+
+    async fn cfg_param_tuple(
+        &self,
+        #[cfg(any())] (left, right): (u8, u8),
+        #[cfg(all())] (_left, _right): (u8, u8),
+    ) {
+    }
 }
 
 pub async fn test() {