Skip to content

Commit 40a544e

Browse files
committed
[Macros] Introduce SwiftSyntaxMacroExpansion module
This module is a unified macro expansion logic shared between 'swift' ASTGen and SwiftCompilerPluginMessageHandling
1 parent 17e83de commit 40a544e

File tree

7 files changed

+253
-157
lines changed

7 files changed

+253
-157
lines changed

Package.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ let package = Package(
5757
.library(name: "SwiftSyntax", targets: ["SwiftSyntax"]),
5858
.library(name: "SwiftSyntaxBuilder", targets: ["SwiftSyntaxBuilder"]),
5959
.library(name: "SwiftSyntaxMacros", targets: ["SwiftSyntaxMacros"]),
60+
.library(name: "SwiftSyntaxMacroExpansion", targets: ["SwiftSyntaxMacroExpansion"]),
6061
.library(name: "SwiftSyntaxMacrosTestSupport", targets: ["SwiftSyntaxMacrosTestSupport"]),
6162
],
6263
targets: [
@@ -113,7 +114,7 @@ let package = Package(
113114

114115
.target(
115116
name: "SwiftCompilerPluginMessageHandling",
116-
dependencies: ["SwiftDiagnostics", "SwiftOperators", "SwiftParser", "SwiftSyntax", "SwiftSyntaxMacros"],
117+
dependencies: ["SwiftDiagnostics", "SwiftOperators", "SwiftParser", "SwiftSyntax", "SwiftSyntaxMacros", "SwiftSyntaxMacroExpansion"],
117118
exclude: ["CMakeLists.txt"]
118119
),
119120

@@ -178,6 +179,12 @@ let package = Package(
178179
exclude: ["CMakeLists.txt"]
179180
),
180181

182+
.target(
183+
name: "SwiftSyntaxMacroExpansion",
184+
dependencies: ["SwiftDiagnostics", "SwiftParser", "SwiftSyntax", "SwiftSyntaxBuilder", "SwiftSyntaxMacros"],
185+
exclude: ["CMakeLists.txt"]
186+
),
187+
181188
.testTarget(
182189
name: "SwiftSyntaxMacrosTest",
183190
dependencies: ["_SwiftSyntaxTestSupport", "SwiftDiagnostics", "SwiftOperators", "SwiftParser", "SwiftSyntaxBuilder", "SwiftSyntaxMacros", "SwiftSyntaxMacrosTestSupport"]

Sources/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,6 @@ add_subdirectory(SwiftParserDiagnostics)
3939
add_subdirectory(SwiftOperators)
4040
add_subdirectory(SwiftSyntaxBuilder)
4141
add_subdirectory(SwiftSyntaxMacros)
42+
add_subdirectory(SwiftSyntaxMacroExpansion)
4243
add_subdirectory(SwiftCompilerPluginMessageHandling)
4344
add_subdirectory(SwiftIDEUtils)

Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ target_link_libraries(SwiftCompilerPluginMessageHandling PUBLIC
2020
SwiftDiagnostics
2121
SwiftParser
2222
SwiftSyntaxMacros
23+
SwiftSyntaxMacroExpansion
2324
SwiftOperators)

Sources/SwiftCompilerPluginMessageHandling/Macros.swift

Lines changed: 30 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import SwiftBasicFormat
1414
import SwiftDiagnostics
1515
import SwiftSyntax
1616
import SwiftSyntaxMacros
17+
import SwiftSyntaxMacroExpansion
1718

1819
extension CompilerPluginMessageHandler {
1920
/// Get concrete macro type from a pair of module name and type name.
@@ -35,7 +36,7 @@ extension CompilerPluginMessageHandler {
3536
expansionDiscriminator: discriminator
3637
)
3738

38-
let expandedSource: String
39+
let expandedSource: String?
3940
do {
4041
guard let macroSyntax = syntax.asProtocol(FreestandingMacroExpansionSyntax.self) else {
4142
throw MacroExpansionError.freestandingMacroSyntaxIsNotMacro
@@ -44,34 +45,14 @@ extension CompilerPluginMessageHandler {
4445
throw MacroExpansionError.macroTypeNotFound
4546
}
4647

47-
switch macroDefinition {
48-
case let exprMacroDef as ExpressionMacro.Type:
49-
func _expand(node: some FreestandingMacroExpansionSyntax) throws -> ExprSyntax {
50-
try exprMacroDef.expansion(of: node, in: context)
51-
}
52-
let rewritten = try _openExistential(macroSyntax, do: _expand)
53-
expandedSource = rewritten.formattedExpansion(macroDefinition.formatMode)
54-
55-
case let declMacroDef as DeclarationMacro.Type:
56-
func _expand(node: some FreestandingMacroExpansionSyntax) throws -> [DeclSyntax] {
57-
try declMacroDef.expansion(of: node, in: context)
58-
}
59-
let rewritten = try _openExistential(macroSyntax, do: _expand)
60-
expandedSource = CodeBlockItemListSyntax(rewritten.map { CodeBlockItemSyntax(item: .decl($0)) }).formattedExpansion(macroDefinition.formatMode)
61-
62-
case let codeItemMacroDef as CodeItemMacro.Type:
63-
func _expand(node: some FreestandingMacroExpansionSyntax) throws -> [CodeBlockItemSyntax] {
64-
try codeItemMacroDef.expansion(of: node, in: context)
65-
}
66-
let rewritten = try _openExistential(macroSyntax, do: _expand)
67-
expandedSource = CodeBlockItemListSyntax(rewritten).formattedExpansion(macroDefinition.formatMode)
68-
69-
default:
70-
throw MacroExpansionError.unmathedMacroRole
71-
}
48+
expandedSource = SwiftSyntaxMacroExpansion.expandFreestandingMacro(
49+
definition: macroDefinition,
50+
node: macroSyntax,
51+
in: context
52+
)
7253
} catch {
7354
context.addDiagnostics(from: error, node: syntax)
74-
expandedSource = ""
55+
expandedSource = nil
7556
}
7657

7758
let diagnostics = context.diagnostics.map {
@@ -99,132 +80,25 @@ extension CompilerPluginMessageHandler {
9980

10081
let attributeNode = sourceManager.add(attributeSyntax).cast(AttributeSyntax.self)
10182
let declarationNode = sourceManager.add(declSyntax).cast(DeclSyntax.self)
83+
let parentDeclNode = parentDeclSyntax.map { sourceManager.add($0).cast(DeclSyntax.self) }
10284

103-
let expandedSources: [String]
85+
let expandedSources: [String]?
10486
do {
10587
guard let macroDefinition = resolveMacro(macro) else {
10688
throw MacroExpansionError.macroTypeNotFound
10789
}
10890

109-
switch (macroDefinition, macroRole) {
110-
case (let attachedMacro as AccessorMacro.Type, .accessor):
111-
let accessors = try attachedMacro.expansion(
112-
of: attributeNode,
113-
providingAccessorsOf: declarationNode,
114-
in: context
115-
)
116-
expandedSources = accessors.map {
117-
$0.formattedExpansion(macroDefinition.formatMode)
118-
}
119-
120-
case (let attachedMacro as MemberAttributeMacro.Type, .memberAttribute):
121-
guard
122-
let parentDeclSyntax = parentDeclSyntax,
123-
let parentDeclGroup = sourceManager.add(parentDeclSyntax).asProtocol(DeclGroupSyntax.self)
124-
else {
125-
// Compiler error: 'parentDecl' is mandatory for MemberAttributeMacro.
126-
throw MacroExpansionError.invalidExpansionMessage
127-
}
128-
129-
// Local function to expand a member atribute macro once we've opened up
130-
// the existential.
131-
func expandMemberAttributeMacro(
132-
_ node: some DeclGroupSyntax
133-
) throws -> [AttributeSyntax] {
134-
return try attachedMacro.expansion(
135-
of: attributeNode,
136-
attachedTo: node,
137-
providingAttributesFor: declarationNode,
138-
in: context
139-
)
140-
}
141-
142-
let attributes = try _openExistential(
143-
parentDeclGroup,
144-
do: expandMemberAttributeMacro
145-
)
146-
147-
// Form a buffer containing an attribute list to return to the caller.
148-
expandedSources = attributes.map {
149-
$0.formattedExpansion(macroDefinition.formatMode)
150-
}
151-
152-
case (let attachedMacro as MemberMacro.Type, .member):
153-
guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self)
154-
else {
155-
// Compiler error: declNode for member macro must be DeclGroupSyntax.
156-
throw MacroExpansionError.invalidExpansionMessage
157-
}
158-
159-
// Local function to expand a member macro once we've opened up
160-
// the existential.
161-
func expandMemberMacro(
162-
_ node: some DeclGroupSyntax
163-
) throws -> [DeclSyntax] {
164-
return try attachedMacro.expansion(
165-
of: attributeNode,
166-
providingMembersOf: node,
167-
in: context
168-
)
169-
}
170-
171-
let members = try _openExistential(declGroup, do: expandMemberMacro)
172-
173-
// Form a buffer of member declarations to return to the caller.
174-
expandedSources = members.map { $0.formattedExpansion(macroDefinition.formatMode) }
175-
176-
case (let attachedMacro as PeerMacro.Type, .peer):
177-
let peers = try attachedMacro.expansion(
178-
of: attributeNode,
179-
providingPeersOf: declarationNode,
180-
in: context
181-
)
182-
183-
// Form a buffer of peer declarations to return to the caller.
184-
expandedSources = peers.map {
185-
$0.formattedExpansion(macroDefinition.formatMode)
186-
}
187-
188-
case (let attachedMacro as ConformanceMacro.Type, .conformance):
189-
guard
190-
let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self),
191-
let identified = declarationNode.asProtocol(IdentifiedDeclSyntax.self)
192-
else {
193-
// Compiler error: type mismatch.
194-
throw MacroExpansionError.invalidExpansionMessage
195-
}
196-
197-
// Local function to expand a conformance macro once we've opened up
198-
// the existential.
199-
func expandConformanceMacro(
200-
_ node: some DeclGroupSyntax
201-
) throws -> [(TypeSyntax, GenericWhereClauseSyntax?)] {
202-
return try attachedMacro.expansion(
203-
of: attributeNode,
204-
providingConformancesOf: node,
205-
in: context
206-
)
207-
}
208-
209-
let conformances = try _openExistential(
210-
declGroup,
211-
do: expandConformanceMacro
212-
)
213-
214-
// Form a buffer of extension declarations to return to the caller.
215-
expandedSources = conformances.map { typeSyntax, whereClause in
216-
let typeName = identified.identifier.trimmedDescription
217-
let protocolName = typeSyntax.trimmedDescription
218-
let whereClause = whereClause?.trimmedDescription ?? ""
219-
return "extension \(typeName) : \(protocolName) \(whereClause) {}"
220-
}
221-
222-
default:
223-
throw MacroExpansionError.unmathedMacroRole
224-
}
91+
expandedSources = SwiftSyntaxMacroExpansion.expandAttachedMacro(
92+
definition: macroDefinition,
93+
macroRole: MacroRole(messageMacroRole: macroRole),
94+
attributeNode: attributeNode,
95+
declarationNode: declarationNode,
96+
parentDeclNode: parentDeclNode,
97+
in: context
98+
)
22599
} catch {
226100
context.addDiagnostics(from: error, node: attributeNode)
227-
expandedSources = []
101+
expandedSources = nil
228102
}
229103

230104
let diagnostics = context.diagnostics.map {
@@ -236,17 +110,17 @@ extension CompilerPluginMessageHandler {
236110
}
237111
}
238112

239-
fileprivate extension SyntaxProtocol {
240-
/// Perform a format if required and then trim any leading/trailing
241-
/// whitespace.
242-
func formattedExpansion(_ mode: FormatMode) -> String {
243-
let formatted: Syntax
244-
switch mode {
245-
case .auto:
246-
formatted = self.formatted()
247-
case .disabled:
248-
formatted = Syntax(self)
113+
private extension MacroRole {
114+
init(messageMacroRole: PluginMessage.MacroRole) {
115+
switch messageMacroRole {
116+
case .expression: self = .expression
117+
case .declaration: self = .declaration
118+
case .accessor: self = .accessor
119+
case .memberAttribute: self = .memberAttribute
120+
case .member: self = .member
121+
case .peer: self = .peer
122+
case .conformance: self = .conformance
123+
case .codeItem: self = .codeItem
249124
}
250-
return formatted.trimmedDescription(matching: { $0.isWhitespace })
251125
}
252126
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
add_swift_host_library(SwiftSyntaxMacroExpansion
2+
MacroExpansion.swift
3+
)
4+
5+
target_link_libraries(SwiftSyntaxMacroExpansion PUBLIC
6+
SwiftSyntax
7+
SwiftSyntaxMacros
8+
SwiftDiagnostics
9+
)
10+

0 commit comments

Comments
 (0)