Skip to content

Commit de9eb3b

Browse files
committed
Compute the lexical context of an accessor macro based on the pre-rewritten node
When an accessor macro contains a closure, it gets rewritten to a new node in `MacroSystem` by calling `super.visit`. That rewritten node doesn’t have any parents (because `SyntaxRewriter` is expecting that node to get inserted into the tree when the node’s parent gets rewritten). This caused us to generate an empty lexical context in these situations. To fix this, generate the lexical context based on the node before we rewrite it. rdar://142639902
1 parent aac9d60 commit de9eb3b

File tree

2 files changed

+66
-16
lines changed

2 files changed

+66
-16
lines changed

Sources/SwiftSyntaxMacroExpansion/MacroSystem.swift

+33-16
Original file line numberDiff line numberDiff line change
@@ -930,23 +930,30 @@ private class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
930930
}
931931

932932
override func visit(_ node: VariableDeclSyntax) -> DeclSyntax {
933-
var node = super.visit(node).cast(VariableDeclSyntax.self)
933+
var rewrittenNode = super.visit(node).cast(VariableDeclSyntax.self)
934934

935-
guard !macroAttributes(attachedTo: DeclSyntax(node), ofType: AccessorMacro.Type.self).isEmpty else {
936-
return DeclSyntax(node)
935+
guard !macroAttributes(attachedTo: DeclSyntax(rewrittenNode), ofType: AccessorMacro.Type.self).isEmpty else {
936+
return DeclSyntax(rewrittenNode)
937937
}
938938

939-
guard node.bindings.count == 1,
940-
var binding = node.bindings.first
939+
guard rewrittenNode.bindings.count == 1,
940+
var binding = rewrittenNode.bindings.first
941941
else {
942942
contextGenerator(Syntax(node)).addDiagnostics(
943943
from: MacroApplicationError.accessorMacroOnVariableWithMultipleBindings,
944-
node: node
944+
node: rewrittenNode
945945
)
946-
return DeclSyntax(node)
946+
return DeclSyntax(rewrittenNode)
947947
}
948948

949-
var expansion = expandAccessors(of: node, existingAccessors: binding.accessorBlock)
949+
// Generate the context based on the node before it was rewritten by calling `super.visit`. If the node was modified
950+
// by `super.visit`, it will not have any parents, which would cause the lexical context to be empty.
951+
let context = contextGenerator(Syntax(node))
952+
var expansion = expandAccessors(
953+
of: rewrittenNode,
954+
context: context,
955+
existingAccessors: binding.accessorBlock
956+
)
950957

951958
if expansion.accessors != binding.accessorBlock {
952959
if binding.accessorBlock == nil {
@@ -966,16 +973,25 @@ private class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
966973
binding.accessorBlock = expansion.accessors
967974
}
968975

969-
node.bindings = [binding]
976+
rewrittenNode.bindings = [binding]
970977
}
971978

972-
return DeclSyntax(node)
979+
return DeclSyntax(rewrittenNode)
973980
}
974981

975982
override func visit(_ node: SubscriptDeclSyntax) -> DeclSyntax {
976-
var node = super.visit(node).cast(SubscriptDeclSyntax.self)
977-
node.accessorBlock = expandAccessors(of: node, existingAccessors: node.accessorBlock).accessors
978-
return DeclSyntax(node)
983+
var rewrittenNode = super.visit(node).cast(SubscriptDeclSyntax.self)
984+
// Generate the context based on the node before it was rewritten by calling `super.visit`. If the node was modified
985+
// by `super.visit`, it will not have any parents, which would cause the lexical context to be empty.
986+
let context = contextGenerator(Syntax(node))
987+
rewrittenNode.accessorBlock =
988+
expandAccessors(
989+
of: rewrittenNode,
990+
context: context,
991+
existingAccessors: rewrittenNode.accessorBlock
992+
)
993+
.accessors
994+
return DeclSyntax(rewrittenNode)
979995
}
980996
}
981997

@@ -1160,6 +1176,7 @@ extension MacroApplication {
11601176
/// removed).
11611177
private func expandAccessors(
11621178
of storage: some DeclSyntaxProtocol,
1179+
context: Context,
11631180
existingAccessors: AccessorBlockSyntax?
11641181
) -> (accessors: AccessorBlockSyntax?, expandsGetSet: Bool) {
11651182
let accessorMacros = macroAttributes(attachedTo: DeclSyntax(storage), ofType: AccessorMacro.Type.self)
@@ -1184,7 +1201,7 @@ extension MacroApplication {
11841201
definition: macro.definition,
11851202
attributeNode: macro.attributeNode,
11861203
attachedTo: DeclSyntax(storage),
1187-
in: contextGenerator(Syntax(storage)),
1204+
in: context,
11881205
indentationWidth: indentationWidth
11891206
) {
11901207
checkExpansions(newAccessors)
@@ -1201,7 +1218,7 @@ extension MacroApplication {
12011218
definition: macro.definition,
12021219
attributeNode: macro.attributeNode,
12031220
attachedTo: DeclSyntax(storage),
1204-
in: contextGenerator(Syntax(storage)),
1221+
in: context,
12051222
indentationWidth: indentationWidth
12061223
) {
12071224
guard case .accessors(let accessorList) = newAccessors.accessors else {
@@ -1220,7 +1237,7 @@ extension MacroApplication {
12201237
}
12211238
}
12221239
} catch {
1223-
contextGenerator(Syntax(storage)).addDiagnostics(from: error, node: macro.attributeNode)
1240+
context.addDiagnostics(from: error, node: macro.attributeNode)
12241241
}
12251242
}
12261243
return (newAccessorsBlock, expandsGetSet)

Tests/SwiftSyntaxMacroExpansionTest/AccessorMacroTests.swift

+33
Original file line numberDiff line numberDiff line change
@@ -532,4 +532,37 @@ final class AccessorMacroTests: XCTestCase {
532532
indentationWidth: indentationWidth
533533
)
534534
}
535+
536+
func testClosureInAccessorMacro() {
537+
enum PropertyWrapperMacro: AccessorMacro {
538+
public static func expansion(
539+
of node: AttributeSyntax,
540+
providingAccessorsOf declaration: some DeclSyntaxProtocol,
541+
in context: some MacroExpansionContext
542+
) throws -> [AccessorDeclSyntax] {
543+
guard let structDecl = context.lexicalContext.first?.as(StructDeclSyntax.self) else {
544+
return []
545+
}
546+
547+
return ["get { \(literal: structDecl.name.text) }"]
548+
}
549+
}
550+
assertMacroExpansion(
551+
"""
552+
struct Foo {
553+
@TestWrapper(b: { a in 1 }) var test3: Thing
554+
}
555+
""",
556+
expandedSource: """
557+
struct Foo {
558+
var test3: Thing {
559+
get {
560+
"Foo"
561+
}
562+
}
563+
}
564+
""",
565+
macros: ["TestWrapper": PropertyWrapperMacro.self]
566+
)
567+
}
535568
}

0 commit comments

Comments
 (0)