Skip to content

Commit 94add47

Browse files
authored
[Macros] Support code item macro expansion (#1442)
1 parent 33e4b33 commit 94add47

File tree

4 files changed

+102
-2
lines changed

4 files changed

+102
-2
lines changed

Sources/SwiftCompilerPluginMessageHandling/Macros.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ extension CompilerPluginMessageHandler {
5858
let rewritten = try _openExistential(macroSyntax, do: _expand)
5959
expandedSource = CodeBlockItemListSyntax(rewritten.map { CodeBlockItemSyntax(item: .decl($0)) }).description
6060

61+
case let codeItemMacroDef as CodeItemMacro.Type:
62+
func _expand<Node: FreestandingMacroExpansionSyntax>(node: Node) throws -> [CodeBlockItemSyntax] {
63+
try codeItemMacroDef.expansion(of: node, in: context)
64+
}
65+
let rewritten = try _openExistential(macroSyntax, do: _expand)
66+
expandedSource = CodeBlockItemListSyntax(rewritten).description
67+
6168
default:
6269
throw MacroExpansionError.unmathedMacroRole
6370
}

Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,13 @@ internal enum PluginToHostMessage: Codable {
8585

8686
enum MacroRole: String, Codable {
8787
case expression
88-
case freeStandingDeclaration
88+
case declaration
8989
case accessor
9090
case memberAttribute
9191
case member
9292
case peer
9393
case conformance
94+
case codeItem
9495
}
9596

9697
struct SourceLocation: Codable {

Sources/SwiftSyntaxMacros/MacroSystem.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,13 @@ class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
132132
let macro = macroSystem.macros[exprExpansion.macro.text]
133133
{
134134
do {
135-
if let macro = macro as? DeclarationMacro.Type {
135+
if let macro = macro as? CodeItemMacro.Type {
136+
let expandedItemList = try macro.expansion(
137+
of: exprExpansion,
138+
in: context
139+
)
140+
newItems.append(contentsOf: expandedItemList)
141+
} else if let macro = macro as? DeclarationMacro.Type {
136142
let expandedItemList = try macro.expansion(
137143
of: exprExpansion,
138144
in: context

Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,59 @@ extension CustomTypeWrapperMacro: AccessorMacro {
618618
}
619619
}
620620

621+
public struct UnwrapMacro: CodeItemMacro {
622+
public static func expansion(
623+
of node: some FreestandingMacroExpansionSyntax,
624+
in context: some MacroExpansionContext
625+
) throws -> [CodeBlockItemSyntax] {
626+
guard !node.argumentList.isEmpty else {
627+
throw CustomError.message("'#unwrap' requires arguments")
628+
}
629+
let errorThrower = node.trailingClosure
630+
let identifiers = try node.argumentList.map { argument in
631+
guard let tupleElement = argument.as(TupleExprElementSyntax.self),
632+
let identifierExpr = tupleElement.expression.as(IdentifierExprSyntax.self)
633+
else {
634+
throw CustomError.message("Arguments must be identifiers")
635+
}
636+
return identifierExpr.identifier
637+
}
638+
639+
func elseBlock(_ token: TokenSyntax) -> CodeBlockSyntax {
640+
let expr: ExprSyntax
641+
if let errorThrower {
642+
expr = """
643+
\(errorThrower)("\(raw: token.text)")
644+
"""
645+
} else {
646+
expr = """
647+
fatalError("'\(raw: token.text)' is nil")
648+
"""
649+
}
650+
return .init(
651+
statements: .init([
652+
.init(
653+
leadingTrivia: " ",
654+
item: .expr(expr),
655+
trailingTrivia: " "
656+
)
657+
])
658+
)
659+
}
660+
661+
return identifiers.map { identifier in
662+
CodeBlockItemSyntax(
663+
item: CodeBlockItemSyntax.Item.stmt(
664+
"""
665+
666+
guard let \(raw: identifier.text) else \(elseBlock(identifier))
667+
"""
668+
)
669+
)
670+
}
671+
}
672+
}
673+
621674
// MARK: Assertion helper functions
622675

623676
/// Assert that expanding the given macros in the original source produces
@@ -685,6 +738,7 @@ public let testMacros: [String: Macro.Type] = [
685738
"wrapAllProperties": WrapAllProperties.self,
686739
"wrapStoredProperties": WrapStoredProperties.self,
687740
"customTypeWrapper": CustomTypeWrapperMacro.self,
741+
"unwrap": UnwrapMacro.self,
688742
]
689743

690744
final class MacroSystemTests: XCTestCase {
@@ -976,4 +1030,36 @@ final class MacroSystemTests: XCTestCase {
9761030
)
9771031

9781032
}
1033+
1034+
func testUnwrap() {
1035+
assertMacroExpansion(
1036+
macros: testMacros,
1037+
#"""
1038+
let x: Int? = 1
1039+
let y: Int? = nil
1040+
let z: Int? = 3
1041+
#unwrap(x, y, z)
1042+
#unwrap(x, y, z) {
1043+
fatalError("nil is \\($0)")
1044+
}
1045+
"""#,
1046+
#"""
1047+
let x: Int? = 1
1048+
let y: Int? = nil
1049+
let z: Int? = 3
1050+
guard let x else { fatalError("'x' is nil") }
1051+
guard let y else { fatalError("'y' is nil") }
1052+
guard let z else { fatalError("'z' is nil") }
1053+
guard let x else { {
1054+
fatalError("nil is \\($0)")
1055+
}("x") }
1056+
guard let y else { {
1057+
fatalError("nil is \\($0)")
1058+
}("y") }
1059+
guard let z else { {
1060+
fatalError("nil is \\($0)")
1061+
}("z") }
1062+
"""#
1063+
)
1064+
}
9791065
}

0 commit comments

Comments
 (0)