SE-0382: Expression Macros

I tried to implement a very rough proof of concept for implementing the result builder transform using an expression macro. In theory, this should be possible. A result builder type and a closure to transform is passed to the macro and a transformed closure is returned. As I test it, however, the compiler gives me an error with the following output:

Macro expansion of #apply(resultBuilder:to:) in /Users/kocki/Downloads/swift-macro-examples/MacroExamples/main.swift:67:21-72:3 as (() -> String)
------------------------------
{ () -> String in
let __macro_local_0 = StringAppender.buildPartialBlock(first: StringAppender.buildExpression("This"));
let __macro_local_1 = StringAppender.buildPartialBlock(accumulated: __macro_local_0, next: StringAppender.buildExpression("is"));
let __macro_local_2 = StringAppender.buildPartialBlock(accumulated: __macro_local_1, next: StringAppender.buildExpression("a"));
let __macro_local_3 = StringAppender.buildPartialBlock(accumulated: __macro_local_2, next: StringAppender.buildExpression("sentence."));
return StringAppender.buildFinalResult(__macro_local_3)
}
------------------------------
Macro expansion of #apply(resultBuilder:to:) in /Users/kocki/Downloads/swift-macro-examples/MacroExamples/main.swift:67:21-72:3:3:69: error: cannot find '__macro_local_0' in scope
let __macro_local_1 = StringAppender.buildPartialBlock(accumulated: __macro_local_0, next: StringAppender.buildExpression("is"));
                                                                    ^~~~~~~~~~~~~~~
Macro expansion of #apply(resultBuilder:to:) in /Users/kocki/Downloads/swift-macro-examples/MacroExamples/main.swift:67:21-72:3:4:69: error: cannot find '__macro_local_1' in scope
let __macro_local_2 = StringAppender.buildPartialBlock(accumulated: __macro_local_1, next: StringAppender.buildExpression("a"));
                                                                    ^~~~~~~~~~~~~~~
Macro expansion of #apply(resultBuilder:to:) in /Users/kocki/Downloads/swift-macro-examples/MacroExamples/main.swift:67:21-72:3:5:69: error: cannot find '__macro_local_2' in scope
let __macro_local_3 = StringAppender.buildPartialBlock(accumulated: __macro_local_2, next: StringAppender.buildExpression("sentence."));
                                                                    ^~~~~~~~~~~~~~~
Macro expansion of #apply(resultBuilder:to:) in /Users/kocki/Downloads/swift-macro-examples/MacroExamples/main.swift:67:21-72:3:6:40: error: cannot find '__macro_local_3' in scope
return StringAppender.buildFinalResult(__macro_local_3)
                                       ^~~~~~~~~~~~~~~

The error persists if I use other variable names.

Full code

In MacroExampleLib:

public macro apply<R: ResultBuilder>(resultBuilder: R.Type, to closure: () -> Void) -> (() -> String) = MacroExamplesPlugin.ResultBuilderMacro // If I use `(() -> R.FinalResult)` as the return type, I get another error

public protocol ResultBuilder {
    associatedtype Component
    associatedtype FinalResult
    
    static func buildPartialBlock(first: Component) -> Component
    
    static func buildPartialBlock(accumulated: Component, next: Component) -> Component
    
    static func buildFinalResult(_ component: Component) -> FinalResult
}

In MacroExamplesPlugin:

public struct ResultBuilderMacro: ExpressionMacro {
    public static func expansion(
        of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext
    ) throws -> ExprSyntax {
        guard
            let resultBuilderSelfExpr = node.argumentList.first?.expression.as(MemberAccessExprSyntax.self),
            let resultBuilderName = resultBuilderSelfExpr.base?.withoutTrivia().description,
            let originalClosure = node.argumentList.dropFirst().first?.expression.as(ClosureExprSyntax.self)
        else {
            throw SomeError()
        }
        
        let originalStatements: [CodeBlockItemSyntax] = Array(originalClosure.statements.map { $0.withoutTrivia() })
        guard let firstStatement = originalStatements.first else {
            throw SomeError()
        }
        
        var localName = context.createUniqueLocalName()
        var newStatements: [String] = []
        newStatements.append("let \(localName) = \(resultBuilderName).buildPartialBlock(first: \(resultBuilderName).buildExpression(\(firstStatement)));")
        
        for statement in originalStatements.dropFirst() {
            let newLocalName = context.createUniqueLocalName()
            newStatements.append("let \(newLocalName) = \(resultBuilderName).buildPartialBlock(accumulated: \(localName), next: \(resultBuilderName).buildExpression(\(statement)));")
            localName = newLocalName
        }
        
        newStatements.append("return \(resultBuilderName).buildFinalResult(\(localName))")
        
        let joinedStatements = newStatements.joined(separator: "\n")
        return "{ () -> String in\n\(raw: joinedStatements)\n}"
    }
}

struct SomeError: Error {}

Finally, in main.swift:

@resultBuilder
struct StringAppender: ResultBuilder {
    static func buildExpression(_ expression: String) -> String {
        expression
    }
    
    static func buildExpression<T>(_ expression: T) -> String {
        String(describing: expression)
    }
    
    static func buildPartialBlock(first: String) -> String {
        first
    }
    
    static func buildPartialBlock(accumulated: String, next: String) -> String {
        accumulated + " " + next
    }
    
    static func buildFinalResult(_ component: String) -> String {
        component
    }
}

@StringAppender
var string: String {
    "This"
    "is"
    "a"
    "sentence."
}

let stringClosure = #apply(resultBuilder: StringAppender.self, to: {
    "This"
    "is"
    "a"
    "sentence."
})

print(string)
print(stringClosure())