ExpressionMacro trailing closure inherits code's original indentation
Opened this issue · 2 comments
Description
If macro is nested in some closure, its trailing closure preserves the original code indentation. So if I build FunctionCallExprSyntax
with trailing closure that takes statements from the original trailing closure, the new statements are incorrectly indented.
This probably applies as well to closure as last argument.
Is there any manual way to trim the statements to decrease the indent?
Steps to Reproduce
assertMacroExpansion(
#"""
VStack {
#SomeMacro(arg1) { output in
EmptyView()
}
}
"""#,
expandedSource: #"""
VStack {
SomeWrapper(arg1) { output in
EmptyView()
}
}
"""#,
macros: testMacros
)
The code above incorrectly expands to:
#"""
VStack {
SomeWrapper(arg1) { output in
EmptyView()
}
}
"""#
Tracked in Apple’s issue tracker as rdar://114755235
Here’s a full test case that reproduces the issue.
func testMultilineIndentedMacroExpression() {
struct TestMacro: ExpressionMacro {
static func expansion(of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) throws -> ExprSyntax {
return ExprSyntax(FunctionCallExprSyntax(
calledExpression: ExprSyntax("Test"),
leftParen: node.leftParen,
arguments: node.argumentList,
rightParen: node.rightParen,
trailingClosure: node.trailingClosure,
additionalTrailingClosures: node.additionalTrailingClosures
))
}
}
assertMacroExpansion(
#"""
func test() {
#Test() { output in
EmptyView()
}
}
"""#,
expandedSource: #"""
func test() {
Test() { output in
EmptyView()
}
}
"""#,
macros: ["Test": TestMacro.self]
)
}
Fixing this is very tricky or even impossible.
We add the indentation level of the macro expression to all lines of the expanded macro expression so that if you have a macro that expands #Test
to
Test {
print("a")
}
and you us #Test
in an indented context like
func test() {
#Test
}
Then the expanded source should add another two spaces even to the print
line so that it’s indented by 4 spaces in the end. And distinguishing between this case and the case described in the issue is impossible in general.
The other alternative to fixing this could be that we strip the indentation of the line that the macro starts at from the syntax tree that we pass to the expansion function but that doesn’t match what the compiler is doing and the idea is that the macro receives the syntax node of the macro as-is.