Macro parameter is compiled before expansion

Hello!
I have a macro that converts expression into a string literal, e.g.:

#toString(variable) -> "variable"
#toString(TypeName) -> "TypeName"
#toString(\TypeName.property) -> "property"

In Xcode 16.3 #toString(TypeName) stopped to work, compilation throws 'Expected member name or initializer call after type name' error. Everything works fine in Xcode 16.2.
The following works in both Xcode versions:

#toString(variable) -> "variable"
#toString(\TypeName.property) -> "property"

Seems like new Xcode tries to compile the code that shouldn't be compiled because macro expansion will replace it with another code.

Does anybody know what new has appeared in Xcode 16.3? Is this behavior correct?
Thank you!

1 Like

Can you show your declaration for macro toString?

@freestanding(expression)
public macro toString(_ expression: Any) -> String = #externalMacro(module: "MacrosForSwiftMacros", type: "ToStringMacro")


public struct ToStringMacro: ExpressionMacro
{
	public enum ToStringError: Error, CustomStringConvertible
	{
		case unsupportedExpression
		
		public var description: String
		{
			switch self
			{
			case .unsupportedExpression:
				return "Unsupported expression."
			}
		}
	}
	
	
	
	public static func expansion(of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) throws -> ExprSyntax
	{
		let argument = node.argumentList.first!.expression
		
		if let declExpr = argument.as(DeclReferenceExprSyntax.self)
		{
			return "\"\(declExpr.baseName)\""
		}
		else if let memberExpr = argument.as(MemberAccessExprSyntax.self)
		{
			return "\"\(memberExpr.declName.baseName)\""
		}
		else if let keyPathExpr = argument.as(KeyPathExprSyntax.self),
						let compExpr = keyPathExpr.components.last?.component.as(KeyPathPropertyComponentSyntax.self)
		{
			return "\"\(compExpr.declName.baseName)\""
		}
		
		throw ToStringError.unsupportedExpression
	}
}

Also, in OP, there is a link to GitHub repo.

I think the reason is that the compiler strengthened the call-site syntax checking. And TypeName as a whole expression is not a valid reference to anything.

You probabibly need to invoke your macro with #toString(TypeName.self), and also adjust the macro implementation.

Thank you for your response, but it doesn't answer the questions.

The behavior is correct: expression macro parameters should be type-checked before the macro expansion is performed.
Actually, this is Xcode 16.3 which does it correctly and fixes some previously missing checks.

1 Like

can you please show what type of error is bring thrown around in 16.3

Actually in the coming days there might be a posibility of changes being made to the macro behaviour at compiler level and if we are able to catch this anomaly beforehand it will be benificial for us when we are making changes.

This one:
Compile error

But as I wrote in my previous post, Xcode 16.3 does everything correctly. I know that now, thanks Apple, they reacted on my feedback on the problem quite swiftly.
Expression macro expects a correct expression and just Int is not correct one. I edited macro to process TypeName.self:

let s = #toString(Int.self)
3 Likes