Skip to content

Commit b04c0da

Browse files
Mx-Irisstackotter
authored andcommitted
Add some Examples
1 parent 042d75d commit b04c0da

File tree

9 files changed

+381
-1
lines changed

9 files changed

+381
-1
lines changed

Sources/MacroToolkitExample/MacroToolkitExample.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,19 @@ public macro CustomCodable() =
3636
@attached(memberAttribute)
3737
public macro DictionaryStorage() =
3838
#externalMacro(module: "MacroToolkitExamplePlugin", type: "DictionaryStorageMacro")
39+
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+
52+
@attached(member, names: arbitrary)
53+
public macro AddAsyncImplementationAllMembers() =
54+
#externalMacro(module: "MacroToolkitExamplePlugin", type: "AddAsyncImplementationAllMembersMacro")
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import SwiftSyntax
2+
import MacroToolkit
3+
import SwiftSyntaxMacros
4+
5+
public enum AddAsyncImplementationAllMembersMacro: MemberMacro {
6+
public static func expansion(of node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: some MacroExpansionContext) throws -> [DeclSyntax] {
7+
declaration.memberBlock.members.map(\.decl).compactMap {
8+
try? AddAsyncImplementationCore.expansion(of: nil, providingFunctionOf: $0)
9+
}
10+
}
11+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import SwiftSyntax
2+
import MacroToolkit
3+
import SwiftSyntaxMacros
4+
5+
// Modified from: https://github.com/DougGregor/swift-macro-examples/blob/f61ac7cdca8dc3557e53f86e7e03df1353908d3e/MacroExamplesPlugin/AddAsyncMacro.swift
6+
7+
enum AddAsyncImplementationCore {
8+
static func expansion(of node: AttributeSyntax?, providingFunctionOf declaration: some DeclSyntaxProtocol) throws -> DeclSyntax {
9+
// Only on functions at the moment.
10+
guard let function = Function(declaration) else {
11+
throw MacroError("@AddAsync only works on functions")
12+
}
13+
14+
// This only makes sense for non async functions.
15+
guard !function.isAsync else {
16+
throw MacroError("@AddAsync requires a non async function")
17+
}
18+
19+
// This only makes sense void functions
20+
guard function.returnsVoid else {
21+
throw MacroError("@AddAsync requires a function that returns void")
22+
}
23+
24+
// Requires a completion handler block as last parameter
25+
guard
26+
let completionHandlerType = function.parameters.last?.type.asFunctionType
27+
else {
28+
throw MacroError(
29+
"@AddAsync requires a function that has a completion handler as last parameter")
30+
}
31+
32+
// Completion handler needs to return Void
33+
guard completionHandlerType.returnType.isVoid else {
34+
throw MacroError(
35+
"@AddAsync requires a function that has a completion handler that returns Void")
36+
}
37+
38+
guard let returnType = completionHandlerType.parameters.first else {
39+
throw MacroError(
40+
"@AddAsync requires a function that has a completion handler that has one parameter"
41+
)
42+
}
43+
44+
// Destructure return type
45+
let successReturnType: Type
46+
let isResultReturn: Bool
47+
if case let .simple("Result", (successType, _)) = destructure(returnType) {
48+
isResultReturn = true
49+
successReturnType = successType
50+
} else {
51+
isResultReturn = false
52+
successReturnType = returnType
53+
}
54+
55+
// Remove completionHandler and comma from the previous parameter
56+
let newParameters = function.parameters.dropLast()
57+
58+
// Drop the @AddAsync attribute from the new declaration.
59+
var filteredAttributes = function.attributes
60+
if let node {
61+
filteredAttributes = filteredAttributes.removing(node)
62+
}
63+
64+
let callArguments = newParameters.asPassthroughArguments
65+
66+
let switchBody: ExprSyntax =
67+
"""
68+
switch returnValue {
69+
case .success(let value):
70+
continuation.resume(returning: value)
71+
case .failure(let error):
72+
continuation.resume(throwing: error)
73+
}
74+
"""
75+
76+
let continuationExpr =
77+
isResultReturn
78+
? "try await withCheckedThrowingContinuation { continuation in"
79+
: "await withCheckedContinuation { continuation in"
80+
81+
let newBody: ExprSyntax =
82+
"""
83+
\(raw: continuationExpr)
84+
\(raw: function.identifier)(\(raw: callArguments.joined(separator: ", "))) { returnValue in
85+
\(isResultReturn ? switchBody : "continuation.resume(returning: returnValue)")
86+
}
87+
}
88+
"""
89+
90+
// TODO: Make better codeblock init
91+
let newFunc =
92+
function._syntax
93+
.withParameters(newParameters)
94+
.withReturnType(successReturnType)
95+
.withAsyncModifier()
96+
.withThrowsModifier(isResultReturn)
97+
.withBody(CodeBlockSyntax([newBody]))
98+
.withAttributes(filteredAttributes)
99+
.withLeadingBlankLine()
100+
101+
return DeclSyntax(newFunc)
102+
}
103+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import SwiftSyntax
2+
import MacroToolkit
3+
import SwiftSyntaxMacros
4+
5+
public enum AddAsyncImplementationMacro: PeerMacro {
6+
public static func expansion(
7+
of node: AttributeSyntax,
8+
providingPeersOf declaration: some DeclSyntaxProtocol,
9+
in context: some MacroExpansionContext
10+
) throws -> [DeclSyntax] {
11+
let decl = try AddAsyncImplementationCore.expansion(of: node, providingFunctionOf: declaration)
12+
return [decl]
13+
}
14+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import SwiftSyntax
2+
import MacroToolkit
3+
import SwiftSyntaxMacros
4+
5+
public enum AddAsyncInterfaceAllMembersMacro: MemberMacro {
6+
public static func expansion(of node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: some MacroExpansionContext) throws -> [DeclSyntax] {
7+
guard let protocolDecl = declaration.as(ProtocolDeclSyntax.self) else {
8+
throw MacroError("Macro `AddAsyncInterfaceToAllMemberMacro` can only be applied to a protocol")
9+
}
10+
11+
let methods = protocolDecl.memberBlock.members.map(\.decl).compactMap {
12+
try? AddAsyncInterfaceCore.expansion(of: nil, providingFunctionOf: $0)
13+
}
14+
return methods
15+
}
16+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import SwiftSyntax
2+
import MacroToolkit
3+
import SwiftSyntaxMacros
4+
5+
enum AddAsyncInterfaceCore {
6+
static func expansion(of node: AttributeSyntax?, providingFunctionOf declaration: some DeclSyntaxProtocol) throws -> DeclSyntax {
7+
// Only on functions at the moment.
8+
guard let function = Function(declaration) else {
9+
throw MacroError("@AddAsync only works on functions")
10+
}
11+
12+
// This only makes sense for non async functions.
13+
guard !function.isAsync else {
14+
throw MacroError("@AddAsync requires a non async function")
15+
}
16+
17+
// This only makes sense void functions
18+
guard function.returnsVoid else {
19+
throw MacroError("@AddAsync requires a function that returns void")
20+
}
21+
22+
// Requires a completion handler block as last parameter
23+
guard
24+
let completionHandlerType = function.parameters.last?.type.asFunctionType
25+
else {
26+
throw MacroError(
27+
"@AddAsync requires a function that has a completion handler as last parameter")
28+
}
29+
30+
// Completion handler needs to return Void
31+
guard completionHandlerType.returnType.isVoid else {
32+
throw MacroError(
33+
"@AddAsync requires a function that has a completion handler that returns Void")
34+
}
35+
36+
guard let returnType = completionHandlerType.parameters.first else {
37+
throw MacroError(
38+
"@AddAsync requires a function that has a completion handler that has one parameter"
39+
)
40+
}
41+
42+
// Destructure return type
43+
let successReturnType: Type
44+
let isResultReturn: Bool
45+
if case let .simple("Result", (successType, _)) = destructure(returnType) {
46+
isResultReturn = true
47+
successReturnType = successType
48+
} else {
49+
isResultReturn = false
50+
successReturnType = returnType
51+
}
52+
53+
// Remove completionHandler and comma from the previous parameter
54+
let newParameters = function.parameters.dropLast()
55+
56+
// Drop the @AddAsync attribute from the new declaration.
57+
var filteredAttributes = function.attributes
58+
if let node {
59+
filteredAttributes = filteredAttributes.removing(node)
60+
}
61+
62+
// TODO: Make better codeblock init
63+
let newFunc =
64+
function._syntax
65+
.withParameters(newParameters)
66+
.withReturnType(successReturnType)
67+
.withAsyncModifier()
68+
.withThrowsModifier(isResultReturn)
69+
.withAttributes(filteredAttributes)
70+
.withLeadingBlankLine()
71+
72+
return DeclSyntax(newFunc)
73+
}
74+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import SwiftSyntax
2+
import MacroToolkit
3+
import SwiftSyntaxMacros
4+
5+
public enum AddAsyncInterfaceMacro: PeerMacro {
6+
public static func expansion(
7+
of node: AttributeSyntax,
8+
providingPeersOf declaration: some DeclSyntaxProtocol,
9+
in context: some MacroExpansionContext
10+
) throws -> [DeclSyntax] {
11+
let decl = try AddAsyncInterfaceCore.expansion(of: node, providingFunctionOf: declaration)
12+
return [decl]
13+
}
14+
}

Sources/MacroToolkitExamplePlugin/MacroToolkitExamplePlugin.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ import SwiftSyntaxMacros
44
@main
55
struct MacroToolkitExamplePlugin: CompilerPlugin {
66
let providingMacros: [Macro.Type] = [
7-
AddAsyncMacro.self
7+
AddAsyncMacro.self,
8+
AddCompletionHandlerMacro.self,
9+
CaseDetectionMacro.self,
10+
AddBlockerMacro.self,
11+
OptionSetMacro.self,
12+
MetaEnumMacro.self,
13+
CustomCodableMacro.self,
14+
CodableKeyMacro.self,
15+
DictionaryStorageMacro.self,
16+
AddAsyncInterfaceMacro.self,
17+
AddAsyncInterfaceAllMembersMacro.self,
18+
AddAsyncImplementationMacro.self,
19+
AddAsyncImplementationAllMembersMacro.self,
820
]
921
}

0 commit comments

Comments
 (0)