Skip to content

Commit 806c473

Browse files
authored
Merge pull request #2163 from Matejkob/macro-examples-unit-tests
Enhancements to Macro Examples: Unit Tests and Code Organization
2 parents 2c847f8 + 919ae85 commit 806c473

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2018
-624
lines changed

Examples/Package.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ let package = Package(
5252
.testTarget(
5353
name: "MacroExamplesImplementationTests",
5454
dependencies: [
55-
"MacroExamplesImplementation"
55+
"MacroExamplesImplementation",
56+
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
57+
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
5658
],
5759
path: "Tests/MacroExamples/Implementation"
5860
),

Examples/Sources/MacroExamples/Implementation/DictionaryIndirectionMacro.swift renamed to Examples/Sources/MacroExamples/Implementation/ComplexMacros/DictionaryIndirectionMacro.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,14 @@ public struct DictionaryStoragePropertyMacro: AccessorMacro {
8181

8282
return [
8383
"""
84-
85-
get {
86-
_storage[\(literal: identifier.text), default: \(defaultValue)] as! \(type)
87-
}
84+
get {
85+
_storage[\(literal: identifier.text), default: \(defaultValue)] as! \(type)
86+
}
8887
""",
8988
"""
90-
91-
set {
92-
_storage[\(literal: identifier.text)] = newValue
93-
}
89+
set {
90+
_storage[\(literal: identifier.text)] = newValue
91+
}
9492
""",
9593
]
9694
}

Examples/Sources/MacroExamples/Implementation/FontLiteralMacro.swift renamed to Examples/Sources/MacroExamples/Implementation/Expression/FontLiteralMacro.swift

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ import SwiftSyntaxMacros
1616
/// Implementation of the `#fontLiteral` macro, which is similar in spirit
1717
/// to the built-in expressions `#colorLiteral`, `#imageLiteral`, etc., but in
1818
/// a small macro.
19-
public struct FontLiteralMacro: ExpressionMacro {
19+
public enum FontLiteralMacro: ExpressionMacro {
2020
public static func expansion(
21-
of macro: some FreestandingMacroExpansionSyntax,
21+
of node: some FreestandingMacroExpansionSyntax,
2222
in context: some MacroExpansionContext
23-
) -> ExprSyntax {
23+
) throws -> ExprSyntax {
2424
let argList = replaceFirstLabel(
25-
of: macro.arguments,
25+
of: node.arguments,
2626
with: "fontLiteralName"
2727
)
2828
return ".init(\(argList))"
@@ -40,6 +40,9 @@ private func replaceFirstLabel(
4040
}
4141

4242
var tuple = tuple
43-
tuple[tuple.startIndex] = firstElement.with(\.label, .identifier(newLabel))
43+
tuple[tuple.startIndex] =
44+
firstElement
45+
.with(\.label, .identifier(newLabel))
46+
.with(\.colon, .colonToken())
4447
return tuple
4548
}

Examples/Sources/MacroExamples/Implementation/StringifyMacro.swift renamed to Examples/Sources/MacroExamples/Implementation/Expression/StringifyMacro.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import SwiftSyntaxMacros
2323
/// will expand to
2424
///
2525
/// (x + y, "x + y")
26-
public struct StringifyMacro: ExpressionMacro {
26+
public enum StringifyMacro: ExpressionMacro {
2727
public static func expansion(
2828
of node: some FreestandingMacroExpansionSyntax,
2929
in context: some MacroExpansionContext

Examples/Sources/MacroExamples/Implementation/URLMacro.swift renamed to Examples/Sources/MacroExamples/Implementation/Expression/URLMacro.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@ import SwiftSyntaxMacros
1616

1717
/// Creates a non-optional URL from a static string. The string is checked to
1818
/// be valid during compile time.
19-
public struct URLMacro: ExpressionMacro {
19+
public enum URLMacro: ExpressionMacro {
2020
public static func expansion(
2121
of node: some FreestandingMacroExpansionSyntax,
2222
in context: some MacroExpansionContext
2323
) throws -> ExprSyntax {
24-
2524
guard let argument = node.arguments.first?.expression,
2625
let segments = argument.as(StringLiteralExprSyntax.self)?.segments,
2726
segments.count == 1,

Examples/Sources/MacroExamples/Implementation/WarningMacro.swift renamed to Examples/Sources/MacroExamples/Implementation/Expression/WarningMacro.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ import SwiftSyntaxMacros
1616

1717
/// Implementation of the `myWarning` macro, which mimics the behavior of the
1818
/// built-in `#warning`.
19-
public struct WarningMacro: ExpressionMacro {
19+
public enum WarningMacro: ExpressionMacro {
2020
public static func expansion(
21-
of macro: some FreestandingMacroExpansionSyntax,
21+
of node: some FreestandingMacroExpansionSyntax,
2222
in context: some MacroExpansionContext
2323
) throws -> ExprSyntax {
24-
guard let firstElement = macro.arguments.first,
24+
guard let firstElement = node.arguments.first,
2525
let stringLiteral = firstElement.expression
2626
.as(StringLiteralExprSyntax.self),
2727
stringLiteral.segments.count == 1,
@@ -32,10 +32,10 @@ public struct WarningMacro: ExpressionMacro {
3232

3333
context.diagnose(
3434
Diagnostic(
35-
node: Syntax(macro),
35+
node: Syntax(node),
3636
message: SimpleDiagnosticMessage(
3737
message: messageString.content.description,
38-
diagnosticID: MessageID(domain: "test", id: "error"),
38+
diagnosticID: MessageID(domain: "test123", id: "error"),
3939
severity: .warning
4040
)
4141
)

Examples/Sources/MacroExamples/Implementation/CaseDetectionMacro.swift renamed to Examples/Sources/MacroExamples/Implementation/Member/CaseDetectionMacro.swift

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,11 @@
1313
import SwiftSyntax
1414
import SwiftSyntaxMacros
1515

16-
extension TokenSyntax {
17-
fileprivate var initialUppercased: String {
18-
let name = self.text
19-
guard let initial = name.first else {
20-
return name
21-
}
22-
23-
return "\(initial.uppercased())\(name.dropFirst())"
24-
}
25-
}
26-
27-
public struct CaseDetectionMacro: MemberMacro {
28-
public static func expansion<
29-
Declaration: DeclGroupSyntax,
30-
Context: MacroExpansionContext
31-
>(
16+
public enum CaseDetectionMacro: MemberMacro {
17+
public static func expansion(
3218
of node: AttributeSyntax,
33-
providingMembersOf declaration: Declaration,
34-
in context: Context
19+
providingMembersOf declaration: some DeclGroupSyntax,
20+
in context: some MacroExpansionContext
3521
) throws -> [DeclSyntax] {
3622
declaration.memberBlock.members
3723
.compactMap { $0.decl.as(EnumCaseDeclSyntax.self) }
@@ -50,3 +36,14 @@ public struct CaseDetectionMacro: MemberMacro {
5036
}
5137
}
5238
}
39+
40+
extension TokenSyntax {
41+
fileprivate var initialUppercased: String {
42+
let name = self.text
43+
guard let initial = name.first else {
44+
return name
45+
}
46+
47+
return "\(initial.uppercased())\(name.dropFirst())"
48+
}
49+
}

Examples/Sources/MacroExamples/Implementation/CustomCodable.swift renamed to Examples/Sources/MacroExamples/Implementation/Member/CustomCodable.swift

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,12 @@
1313
import SwiftSyntax
1414
import SwiftSyntaxMacros
1515

16-
public struct CustomCodable: MemberMacro {
17-
16+
public enum CustomCodable: MemberMacro {
1817
public static func expansion(
1918
of node: AttributeSyntax,
2019
providingMembersOf declaration: some DeclGroupSyntax,
2120
in context: some MacroExpansionContext
2221
) throws -> [DeclSyntax] {
23-
2422
let memberList = declaration.memberBlock.members
2523

2624
let cases = memberList.compactMap({ member -> String? in
@@ -46,15 +44,22 @@ public struct CustomCodable: MemberMacro {
4644
})
4745

4846
let codingKeys: DeclSyntax = """
49-
50-
enum CodingKeys: String, CodingKey {
51-
\(raw: cases.joined(separator: "\n"))
52-
}
53-
47+
enum CodingKeys: String, CodingKey {
48+
\(raw: cases.joined(separator: "\n"))
49+
}
5450
"""
5551

56-
return [
57-
codingKeys
58-
]
52+
return [codingKeys]
53+
}
54+
}
55+
56+
public struct CodableKey: PeerMacro {
57+
public static func expansion(
58+
of node: AttributeSyntax,
59+
providingPeersOf declaration: some DeclSyntaxProtocol,
60+
in context: some MacroExpansionContext
61+
) throws -> [DeclSyntax] {
62+
// Does nothing, used only to decorate members with data
63+
return []
5964
}
6065
}

Examples/Sources/MacroExamples/Implementation/MetaEnumMacro.swift renamed to Examples/Sources/MacroExamples/Implementation/Member/MetaEnumMacro.swift

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,36 +41,39 @@ public struct MetaEnumMacro {
4141

4242
func makeMetaEnum() -> DeclSyntax {
4343
// FIXME: Why does this need to be a string to make trailing trivia work properly?
44-
let caseDecls = childCases.map { childCase in
45-
" case \(childCase.name)"
46-
}.joined(separator: "\n")
44+
let caseDecls =
45+
childCases
46+
.map { childCase in
47+
" case \(childCase.name)"
48+
}
49+
.joined(separator: "\n")
4750

4851
return """
49-
50-
\(access)enum Meta {
52+
\(access)enum Meta {
5153
\(raw: caseDecls)
5254
\(makeMetaInit())
53-
}
54-
55+
}
5556
"""
5657
}
5758

5859
func makeMetaInit() -> DeclSyntax {
5960
// FIXME: Why does this need to be a string to make trailing trivia work properly?
60-
let caseStatements = childCases.map { childCase in
61-
"""
62-
case .\(childCase.name):
63-
self = .\(childCase.name)
64-
"""
65-
}.joined(separator: "\n")
61+
let caseStatements =
62+
childCases
63+
.map { childCase in
64+
"""
65+
case .\(childCase.name):
66+
self = .\(childCase.name)
67+
"""
68+
}
69+
.joined(separator: "\n")
6670

6771
return """
68-
69-
\(access)init(_ \(parentParamName): \(parentTypeName)) {
70-
switch \(parentParamName) {
72+
\(access)init(_ \(parentParamName): \(parentTypeName)) {
73+
switch \(parentParamName) {
7174
\(raw: caseStatements)
72-
}
73-
}
75+
}
76+
}
7477
"""
7578
}
7679
}

Examples/Sources/MacroExamples/Implementation/AddAsyncMacro.swift renamed to Examples/Sources/MacroExamples/Implementation/Peer/AddAsyncMacro.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,12 @@ public struct AddAsyncMacro: PeerMacro {
101101

102102
let switchBody: ExprSyntax =
103103
"""
104-
switch returnValue {
105-
case .success(let value):
106-
continuation.resume(returning: value)
107-
case .failure(let error):
108-
continuation.resume(throwing: error)
109-
}
104+
switch returnValue {
105+
case .success(let value):
106+
continuation.resume(returning: value)
107+
case .failure(let error):
108+
continuation.resume(throwing: error)
109+
}
110110
"""
111111

112112
let newBody: ExprSyntax =
@@ -115,8 +115,7 @@ public struct AddAsyncMacro: PeerMacro {
115115
\(raw: isResultReturn ? "try await withCheckedThrowingContinuation { continuation in" : "await withCheckedContinuation { continuation in")
116116
\(raw: funcDecl.name)(\(raw: callArguments.joined(separator: ", "))) { \(raw: returnType != nil ? "returnValue in" : "")
117117
118-
\(raw: isResultReturn ? switchBody : "continuation.resume(returning: \(raw: returnType != nil ? "returnValue" : "()"))")
119-
118+
\(raw: isResultReturn ? switchBody : "continuation.resume(returning: \(raw: returnType != nil ? "returnValue" : "()"))")
120119
}
121120
}
122121

Examples/Sources/MacroExamples/Interface/Macros.swift renamed to Examples/Sources/MacroExamples/Interface/ComplexMacros.swift

Lines changed: 4 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -10,46 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
import Foundation
14-
15-
/// "Stringify" the provided value and produce a tuple that includes both the
16-
/// original value as well as the source code that generated it.
17-
@freestanding(expression) public macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "MacroExamplesImplementation", type: "StringifyMacro")
18-
19-
/// Macro that produces a warning on "+" operators within the expression, and
20-
/// suggests changing them to "-".
21-
@freestanding(expression) public macro addBlocker<T>(_ value: T) -> T = #externalMacro(module: "MacroExamplesImplementation", type: "AddBlocker")
22-
23-
/// Macro that produces a warning, as a replacement for the built-in
24-
/// #warning("...").
25-
@freestanding(expression) public macro myWarning(_ message: String) = #externalMacro(module: "MacroExamplesImplementation", type: "WarningMacro")
26-
27-
public enum FontWeight {
28-
case thin
29-
case normal
30-
case medium
31-
case semiBold
32-
case bold
33-
}
34-
35-
public protocol ExpressibleByFontLiteral {
36-
init(fontLiteralName: String, size: Int, weight: FontWeight)
37-
}
38-
39-
/// Font literal similar to, e.g., #colorLiteral.
40-
@freestanding(expression) public macro fontLiteral<T>(name: String, size: Int, weight: FontWeight) -> T =
41-
#externalMacro(module: "MacroExamplesImplementation", type: "FontLiteralMacro")
42-
where T: ExpressibleByFontLiteral
43-
44-
/// Check if provided string literal is a valid URL and produce a non-optional
45-
/// URL value. Emit error otherwise.
46-
@freestanding(expression) public macro URL(_ stringLiteral: String) -> URL = #externalMacro(module: "MacroExamplesImplementation", type: "URLMacro")
47-
48-
/// Apply the specified attribute to each of the stored properties within the
49-
/// type or member to which the macro is attached. The string can be
50-
/// any attribute (without the `@`).
51-
@attached(memberAttribute)
52-
public macro wrapStoredProperties(_ attributeName: String) = #externalMacro(module: "MacroExamplesImplementation", type: "WrapStoredPropertiesMacro")
13+
// MARK: - Dictionary Storage
5314

5415
/// Wrap up the stored properties of the given type in a dictionary,
5516
/// turning them into computed properties.
@@ -66,6 +27,8 @@ public macro DictionaryStorage() = #externalMacro(module: "MacroExamplesImplemen
6627
@attached(accessor)
6728
public macro DictionaryStorageProperty() = #externalMacro(module: "MacroExamplesImplementation", type: "DictionaryStoragePropertyMacro")
6829

30+
// MARK: - Observable
31+
6932
public protocol Observable {}
7033

7134
public protocol Observer<Subject> {
@@ -108,29 +71,7 @@ public macro Observable() = #externalMacro(module: "MacroExamplesImplementation"
10871
@attached(accessor)
10972
public macro ObservableProperty() = #externalMacro(module: "MacroExamplesImplementation", type: "ObservablePropertyMacro")
11073

111-
/// Adds a "completionHandler" variant of an async function, which creates a new
112-
/// task , calls thh original async function, and delivers its result to the completion
113-
/// handler.
114-
@attached(peer, names: overloaded)
115-
public macro AddCompletionHandler() =
116-
#externalMacro(module: "MacroExamplesImplementation", type: "AddCompletionHandlerMacro")
117-
118-
@attached(peer, names: overloaded)
119-
public macro AddAsync() =
120-
#externalMacro(module: "MacroExamplesImplementation", type: "AddAsyncMacro")
121-
122-
/// Add computed properties named `is<Case>` for each case element in the enum.
123-
@attached(member, names: arbitrary)
124-
public macro CaseDetection() = #externalMacro(module: "MacroExamplesImplementation", type: "CaseDetectionMacro")
125-
126-
@attached(member, names: named(Meta))
127-
public macro MetaEnum() = #externalMacro(module: "MacroExamplesImplementation", type: "MetaEnumMacro")
128-
129-
@attached(peer)
130-
public macro CodableKey(name: String) = #externalMacro(module: "MacroExamplesImplementation", type: "CodableKey")
131-
132-
@attached(member, names: named(CodingKeys))
133-
public macro CustomCodable() = #externalMacro(module: "MacroExamplesImplementation", type: "CustomCodable")
74+
// MARK: - Option Set
13475

13576
/// Create an option set from a struct that contains a nested `Options` enum.
13677
///

0 commit comments

Comments
 (0)