Skip to content

Commit fd232f9

Browse files
authored
Merge pull request #2936 from ahoppen/closure-in-accessor
Compute the lexical context of an accessor macro based on the pre-rewritten node
2 parents f0db832 + de9eb3b commit fd232f9

File tree

2 files changed

+66
-16
lines changed

2 files changed

+66
-16
lines changed

Diff for: 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)

Diff for: 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)