Skip to content

Commit 9d139f3

Browse files
Mx-Irisstackotter
authored andcommitted
Remove duplicate code and optimize AddAsync macros
1 parent b04c0da commit 9d139f3

10 files changed

+34
-254
lines changed

Sources/MacroToolkitExample/MacroToolkitExample.swift

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,6 @@ public macro CustomCodable() =
3737
public macro DictionaryStorage() =
3838
#externalMacro(module: "MacroToolkitExamplePlugin", type: "DictionaryStorageMacro")
3939

40-
@attached(peer, names: overloaded)
41-
public macro AddAsyncInterface() =
42-
#externalMacro(module: "MacroToolkitExamplePlugin", type: "AddAsyncInterfaceMacro")
43-
44-
@attached(member, names: arbitrary)
45-
public macro AddAsyncInterfaceAllMembers() =
46-
#externalMacro(module: "MacroToolkitExamplePlugin", type: "AddAsyncInterfaceAllMembersMacro")
47-
48-
@attached(peer, names: overloaded)
49-
public macro AddAsyncImplementation() =
50-
#externalMacro(module: "MacroToolkitExamplePlugin", type: "AddAsyncImplementationMacro")
51-
5240
@attached(member, names: arbitrary)
53-
public macro AddAsyncImplementationAllMembers() =
54-
#externalMacro(module: "MacroToolkitExamplePlugin", type: "AddAsyncImplementationAllMembersMacro")
41+
public macro AddAsyncAllMembers() =
42+
#externalMacro(module: "MacroToolkitExamplePlugin", type: "AddAsyncAllMembersMacro")

Sources/MacroToolkitExamplePlugin/AddAsyncImplementationAllMembersMacro.swift renamed to Sources/MacroToolkitExamplePlugin/AddAsyncAllMembersMacro.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import SwiftSyntax
22
import MacroToolkit
33
import SwiftSyntaxMacros
44

5-
public enum AddAsyncImplementationAllMembersMacro: MemberMacro {
5+
public enum AddAsyncAllMembersMacro: MemberMacro {
66
public static func expansion(of node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: some MacroExpansionContext) throws -> [DeclSyntax] {
77
declaration.memberBlock.members.map(\.decl).compactMap {
8-
try? AddAsyncImplementationCore.expansion(of: nil, providingFunctionOf: $0)
8+
try? AddAsyncMacroCore.expansion(of: nil, providingFunctionOf: $0)
99
}
1010
}
1111
}

Sources/MacroToolkitExamplePlugin/AddAsyncImplementationMacro.swift

Lines changed: 0 additions & 14 deletions
This file was deleted.

Sources/MacroToolkitExamplePlugin/AddAsyncInterfaceAllMemberMacro.swift

Lines changed: 0 additions & 16 deletions
This file was deleted.

Sources/MacroToolkitExamplePlugin/AddAsyncInterfaceCore.swift

Lines changed: 0 additions & 74 deletions
This file was deleted.

Sources/MacroToolkitExamplePlugin/AddAsyncInterfaceMacro.swift

Lines changed: 0 additions & 14 deletions
This file was deleted.

Sources/MacroToolkitExamplePlugin/AddAsyncMacro.swift

Lines changed: 2 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import SwiftSyntax
33
import SwiftSyntaxMacros
44

55
// Modified from: https://github.com/DougGregor/swift-macro-examples/blob/f61ac7cdca8dc3557e53f86e7e03df1353908d3e/MacroExamplesPlugin/AddAsyncMacro.swift
6-
public struct AddAsyncMacro: PeerMacro {
6+
public enum AddAsyncMacro: PeerMacro {
77
public static func expansion<
88
Context: MacroExpansionContext,
99
Declaration: DeclSyntaxProtocol
@@ -12,95 +12,6 @@ public struct AddAsyncMacro: PeerMacro {
1212
providingPeersOf declaration: Declaration,
1313
in context: Context
1414
) throws -> [DeclSyntax] {
15-
// Only on functions at the moment.
16-
guard let function = Function(declaration) else {
17-
throw MacroError("@AddAsync only works on functions")
18-
}
19-
20-
// This only makes sense for non async functions.
21-
guard !function.isAsync else {
22-
throw MacroError("@AddAsync requires a non async function")
23-
}
24-
25-
// This only makes sense void functions
26-
guard function.returnsVoid else {
27-
throw MacroError("@AddAsync requires a function that returns void")
28-
}
29-
30-
// Requires a completion handler block as last parameter
31-
guard
32-
let completionHandlerType = function.parameters.last?.type.asFunctionType
33-
else {
34-
throw MacroError(
35-
"@AddAsync requires a function that has a completion handler as last parameter")
36-
}
37-
38-
// Completion handler needs to return Void
39-
guard completionHandlerType.returnType.isVoid else {
40-
throw MacroError(
41-
"@AddAsync requires a function that has a completion handler that returns Void")
42-
}
43-
44-
guard let returnType = completionHandlerType.parameters.first else {
45-
throw MacroError(
46-
"@AddAsync requires a function that has a completion handler that has one parameter"
47-
)
48-
}
49-
50-
// Destructure return type
51-
let successReturnType: Type
52-
let isResultReturn: Bool
53-
if case let .simple("Result", (successType, _)) = destructure(returnType) {
54-
isResultReturn = true
55-
successReturnType = successType
56-
} else {
57-
isResultReturn = false
58-
successReturnType = returnType
59-
}
60-
61-
// Remove completionHandler and comma from the previous parameter
62-
let newParameters = function.parameters.dropLast()
63-
64-
// Drop the @AddAsync attribute from the new declaration.
65-
let filteredAttributes = function.attributes.removing(node)
66-
67-
let callArguments = newParameters.asPassthroughArguments
68-
69-
let switchBody: ExprSyntax =
70-
"""
71-
switch returnValue {
72-
case .success(let value):
73-
continuation.resume(returning: value)
74-
case .failure(let error):
75-
continuation.resume(throwing: error)
76-
}
77-
"""
78-
79-
let continuationExpr =
80-
isResultReturn
81-
? "try await withCheckedThrowingContinuation { continuation in"
82-
: "await withCheckedContinuation { continuation in"
83-
84-
let newBody: ExprSyntax =
85-
"""
86-
\(raw: continuationExpr)
87-
\(raw: function.identifier)(\(raw: callArguments.joined(separator: ", "))) { returnValue in
88-
\(isResultReturn ? switchBody : "continuation.resume(returning: returnValue)")
89-
}
90-
}
91-
"""
92-
93-
// TODO: Make better codeblock init
94-
let newFunc =
95-
function._syntax
96-
.withParameters(newParameters)
97-
.withReturnType(successReturnType)
98-
.withAsyncModifier()
99-
.withThrowsModifier(isResultReturn)
100-
.withBody(CodeBlockSyntax([newBody]))
101-
.withAttributes(filteredAttributes)
102-
.withLeadingBlankLine()
103-
104-
return [DeclSyntax(newFunc)]
15+
[try AddAsyncMacroCore.expansion(of: node, providingFunctionOf: declaration)]
10516
}
10617
}

Sources/MacroToolkitExamplePlugin/AddAsyncImplementationCore.swift renamed to Sources/MacroToolkitExamplePlugin/AddAsyncMacroCore.swift

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import SwiftSyntaxMacros
44

55
// Modified from: https://github.com/DougGregor/swift-macro-examples/blob/f61ac7cdca8dc3557e53f86e7e03df1353908d3e/MacroExamplesPlugin/AddAsyncMacro.swift
66

7-
enum AddAsyncImplementationCore {
7+
enum AddAsyncMacroCore {
88
static func expansion(of node: AttributeSyntax?, providingFunctionOf declaration: some DeclSyntaxProtocol) throws -> DeclSyntax {
99
// Only on functions at the moment.
1010
guard let function = Function(declaration) else {
@@ -63,7 +63,8 @@ enum AddAsyncImplementationCore {
6363

6464
let callArguments = newParameters.asPassthroughArguments
6565

66-
let switchBody: ExprSyntax =
66+
let newBody = function._syntax.body.map { _ in
67+
let switchBody: ExprSyntax =
6768
"""
6869
switch returnValue {
6970
case .success(let value):
@@ -72,31 +73,35 @@ enum AddAsyncImplementationCore {
7273
continuation.resume(throwing: error)
7374
}
7475
"""
75-
76-
let continuationExpr =
76+
77+
let continuationExpr =
7778
isResultReturn
78-
? "try await withCheckedThrowingContinuation { continuation in"
79-
: "await withCheckedContinuation { continuation in"
80-
81-
let newBody: ExprSyntax =
79+
? "try await withCheckedThrowingContinuation { continuation in"
80+
: "await withCheckedContinuation { continuation in"
81+
82+
let newBody: ExprSyntax =
8283
"""
8384
\(raw: continuationExpr)
8485
\(raw: function.identifier)(\(raw: callArguments.joined(separator: ", "))) { returnValue in
8586
\(isResultReturn ? switchBody : "continuation.resume(returning: returnValue)")
8687
}
8788
}
8889
"""
89-
90+
return CodeBlockSyntax([newBody])
91+
}
9092
// TODO: Make better codeblock init
91-
let newFunc =
93+
var newFunc =
9294
function._syntax
93-
.withParameters(newParameters)
94-
.withReturnType(successReturnType)
95-
.withAsyncModifier()
96-
.withThrowsModifier(isResultReturn)
97-
.withBody(CodeBlockSyntax([newBody]))
98-
.withAttributes(filteredAttributes)
99-
.withLeadingBlankLine()
95+
.withParameters(newParameters)
96+
.withReturnType(successReturnType)
97+
.withAsyncModifier()
98+
.withThrowsModifier(isResultReturn)
99+
.withAttributes(filteredAttributes)
100+
.withLeadingBlankLine()
101+
102+
if let newBody {
103+
newFunc = newFunc.withBody(newBody)
104+
}
100105

101106
return DeclSyntax(newFunc)
102107
}

Sources/MacroToolkitExamplePlugin/MacroToolkitExamplePlugin.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ struct MacroToolkitExamplePlugin: CompilerPlugin {
1313
CustomCodableMacro.self,
1414
CodableKeyMacro.self,
1515
DictionaryStorageMacro.self,
16-
AddAsyncInterfaceMacro.self,
17-
AddAsyncInterfaceAllMembersMacro.self,
18-
AddAsyncImplementationMacro.self,
19-
AddAsyncImplementationAllMembersMacro.self,
16+
AddAsyncAllMembersMacro.self,
2017
]
2118
}

Tests/MacroToolkitTests/MacroToolkitTests.swift

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ let testMacros: [String: Macro.Type] = [
1616
"CustomCodable": CustomCodableMacro.self,
1717
"CodableKey": CodableKeyMacro.self,
1818
"DictionaryStorage": DictionaryStorageMacro.self,
19-
"AddAsyncInterface": AddAsyncInterfaceMacro.self,
20-
"AddAsyncInterfaceAllMembers": AddAsyncInterfaceAllMembersMacro.self,
21-
"AddAsyncImplementation": AddAsyncImplementationMacro.self,
22-
"AddAsyncImplementationAllMembers": AddAsyncImplementationAllMembersMacro.self,
19+
"AddAsyncAllMembers": AddAsyncAllMembersMacro.self,
2320
]
2421

2522
final class MacroToolkitTests: XCTestCase {
@@ -570,7 +567,7 @@ final class MacroToolkitTests: XCTestCase {
570567
assertMacroExpansion(
571568
"""
572569
protocol API {
573-
@AddAsyncInterface
570+
@AddAsync
574571
func request(completion: (Int) -> Void)
575572
}
576573
""",
@@ -589,7 +586,7 @@ final class MacroToolkitTests: XCTestCase {
589586
func testAsyncInterfaceAllMembersMacro() throws {
590587
assertMacroExpansion(
591588
"""
592-
@AddAsyncInterfaceAllMembers
589+
@AddAsyncAllMembers
593590
protocol API {
594591
func request1(completion: (Int) -> Void)
595592
func request2(completion: (String) -> Void)
@@ -613,7 +610,7 @@ final class MacroToolkitTests: XCTestCase {
613610
assertMacroExpansion(
614611
"""
615612
struct Client {
616-
@AddAsyncImplementation
613+
@AddAsync
617614
func request1(completion: (Int) -> Void) {
618615
completion(0)
619616
}
@@ -641,7 +638,7 @@ final class MacroToolkitTests: XCTestCase {
641638
func testAsyncImplementationAllMembersMacro() throws {
642639
assertMacroExpansion(
643640
"""
644-
@AddAsyncImplementationAllMembers
641+
@AddAsyncAllMembers
645642
struct Client {
646643
func request1(completion: (Int) -> Void) {
647644
completion(0)

0 commit comments

Comments
 (0)