Skip to content

Commit f92ff9b

Browse files
committed
Support code item macro expansion
1 parent 9764512 commit f92ff9b

File tree

4 files changed

+91
-2
lines changed

4 files changed

+91
-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: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,48 @@ 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("'#UnwrapMacro' 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) else {
633+
throw CustomError.message("Arguments must be identifiers")
634+
}
635+
return identifierExpr.identifier
636+
}
637+
638+
func elseBlock(_ token: TokenSyntax) -> CodeBlockSyntax {
639+
let expr: ExprSyntax
640+
if let errorThrower {
641+
expr = """
642+
\(errorThrower)("\(raw: token.text)")
643+
"""
644+
} else {
645+
expr = """
646+
fatalError("'\(raw: token.text)' is nil")
647+
"""
648+
}
649+
return .init(statements: .init([.init(
650+
leadingTrivia: " ", item: .expr(expr), trailingTrivia: " ")]))
651+
}
652+
653+
return identifiers.map { identifier in
654+
CodeBlockItemSyntax(item: CodeBlockItemSyntax.Item.stmt(
655+
"""
656+
657+
guard let \(raw: identifier.text) else \(elseBlock(identifier))
658+
"""))
659+
}
660+
}
661+
}
662+
621663
// MARK: Assertion helper functions
622664

623665
/// Assert that expanding the given macros in the original source produces
@@ -685,6 +727,7 @@ public let testMacros: [String: Macro.Type] = [
685727
"wrapAllProperties": WrapAllProperties.self,
686728
"wrapStoredProperties": WrapStoredProperties.self,
687729
"customTypeWrapper": CustomTypeWrapperMacro.self,
730+
"unwrap": UnwrapMacro.self
688731
]
689732

690733
final class MacroSystemTests: XCTestCase {
@@ -976,4 +1019,36 @@ final class MacroSystemTests: XCTestCase {
9761019
)
9771020

9781021
}
1022+
1023+
func testUnwrap() {
1024+
AssertMacroExpansion(
1025+
macros: testMacros,
1026+
"""
1027+
let x: Int? = 1
1028+
let y: Int? = nil
1029+
let z: Int? = 3
1030+
#unwrap(x, y, z)
1031+
#unwrap(x, y, z) {
1032+
fatalError("nil is \\($0)")
1033+
}
1034+
""",
1035+
"""
1036+
let x: Int? = 1
1037+
let y: Int? = nil
1038+
let z: Int? = 3
1039+
guard let x else { fatalError("'x' is nil") }
1040+
guard let y else { fatalError("'y' is nil") }
1041+
guard let z else { fatalError("'z' is nil") }
1042+
guard let x else { {
1043+
fatalError("nil is \\($0)")
1044+
}("x") }
1045+
guard let y else { {
1046+
fatalError("nil is \\($0)")
1047+
}("y") }
1048+
guard let z else { {
1049+
fatalError("nil is \\($0)")
1050+
}("z") }
1051+
"""
1052+
)
1053+
}
9791054
}

0 commit comments

Comments
 (0)