'accessor' macro cannot be attached to a parameter

With Xcode Beta 6 I'm getting this error using macros on parameters of functions in a protocol. This wasn't an error in previous beta versions.

It's in a library I built that creates a type safe HTTP client, using macros on protocols.

@URLForm
@POST("/todos/:id/tags")
func createTag(@Path id: Int) async throws // 🛑 'accessor' macro cannot be attached to parameter
@attached(accessor)
public macro Path(_ key: String? = nil) = #externalMacro(module: "PapyrusPlugin", type: "DecoratorMacro")

The macro does nothing - it's purely for decoration so a different macro can detect what code to generate.

It seems like no other macro types work either - Is this intended behavior? If not, can we remove this restriction? Since @propertyWrappers are not available on protocol functions, if this error is permanent it seems like the end of the line for this library.

2 Likes

From outside, that seems like a correct error for accessors macros, but a good argument for “arbitrary declaration or type macro that is known by the compiler to expand to nothing”.

5 Likes

The change that made your code not compile is [5.9][Macros] Add missing macro validation. by hborla · Pull Request #67387 · apple/swift · GitHub (specifically the second commit: Diagnose macros attached to declarations they cannot apply to), following Jordan's reasoning:

There is no existing macro role that makes sense to apply to function parameters. I think we should have such a macro, but it should be a new kind of macro role -- either one that explicitly does nothing like Jordan suggests, or some other kind of macro role that's conceptually similar to a peer macro but generates peers inside the function body (which would also allow mimicking property wrappers attached to function arguments). Perhaps this should be explored in the pitch discussion for function body macros.

3 Likes

Thanks for the explanation @hborla! I went ahead and posted in that thread.

Out of curiosity are you aware of any workaround to annotate a protocol function parameter for the given use case? It would be awesome to get the library working for when 5.9 is released in a few weeks - but I also understand if it isn't feasible until a future release - tons of great new stuff in 5.9 already :rocket:

1 Like

...you could try making a property wrapper that doesn't do any transformation just to let you write the attribute on the parameter :slightly_smiling_face:

1 Like

Oo good idea - unfortunately it doesn't seem like property wrappers are available on protocols, yet?

I'm hoping to keep the interface as a protocol to mirror what retrofit does.

Ah, sorry I had forgotten you mentioned that exact workaround in the original post since this is a protocol requirement! I don’t know of any workaround that allows you to write a custom attribute on a parameter of a protocol requirement.

1 Like

If you'd like to provide the parameter annotation functionality now under a different syntax, you could use a typealias instead of a custom attribute for the parameter type, e.g.

typealias Path<T> = T

// in the protocol
func createTag(id: Path<Int>) async throws

And your macro can look at the type annotation for the parameter. You could deprecate the typealias once the custom attribute syntax becomes available. That's the only option I can think of.

2 Likes

Excellent idea - I was thinking generics were the way to go and a typealias makes it even cleaner. Thank you!

1 Like

Might be a bit late to the party but Xcode 15 beta 8 now gives the error:

'accessor' macro cannot be attached to parameter

Yes - it looks like there's no longer support for any macros on function parameters. Hopefully in the future, but in the meantime one can use either:

  1. A @propertyWrapper
  2. A generic type, if property wrappers aren't supported (i.e. on a protocol) and you just need something to decorate the parameter.
1 Like