Skip to content

Commit d20f066

Browse files
committed
[Macros] ConformanceMacro refines ExtensionMacro.
This changes the ConformanceMacro protocol to refine ExtensionMacro, and provide a default implementation of ExtensionMacro.expansion that calls the conformance expansion method. This way, conformance macros and extension macros can share the same expansion code path.
1 parent 7617b70 commit d20f066

File tree

3 files changed

+41
-64
lines changed

3 files changed

+41
-64
lines changed

Sources/SwiftSyntaxMacroExpansion/MacroExpansion.swift

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -266,42 +266,6 @@ public func expandAttachedMacroWithoutCollapsing<Context: MacroExpansionContext>
266266
$0.formattedExpansion(definition.formatMode)
267267
}
268268

269-
case (let attachedMacro as ConformanceMacro.Type, .conformance):
270-
guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self) else {
271-
// Compiler error: type mismatch.
272-
throw MacroExpansionError.declarationNotDeclGroup
273-
}
274-
guard let identified = declarationNode.asProtocol(IdentifiedDeclSyntax.self)
275-
else {
276-
// Compiler error: type mismatch.
277-
throw MacroExpansionError.declarationNotIdentified
278-
}
279-
280-
// Local function to expand a conformance macro once we've opened up
281-
// the existential.
282-
func expandConformanceMacro(
283-
_ node: some DeclGroupSyntax
284-
) throws -> [(TypeSyntax, GenericWhereClauseSyntax?)] {
285-
return try attachedMacro.expansion(
286-
of: attributeNode,
287-
providingConformancesOf: node,
288-
in: context
289-
)
290-
}
291-
292-
let conformances = try _openExistential(
293-
declGroup,
294-
do: expandConformanceMacro
295-
)
296-
297-
// Form a buffer of extension declarations to return to the caller.
298-
return conformances.map { typeSyntax, whereClause in
299-
let typeName = identified.identifier.trimmedDescription
300-
let protocolName = typeSyntax.trimmedDescription
301-
let whereClause = whereClause?.trimmedDescription ?? ""
302-
return "extension \(typeName) : \(protocolName) \(whereClause) {}"
303-
}
304-
305269
case (let attachedMacro as ExtensionMacro.Type, .extension):
306270
guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self) else {
307271
// Compiler error: type mismatch.

Sources/SwiftSyntaxMacros/MacroProtocols/ConformanceMacro.swift

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import SwiftSyntax
1414

1515
/// Describes a macro that can add conformances to the declaration it's
1616
/// attached to.
17-
public protocol ConformanceMacro: AttachedMacro {
17+
public protocol ConformanceMacro: ExtensionMacro {
1818
/// Expand an attached conformance macro to produce a set of conformances.
1919
///
2020
/// - Parameters:
@@ -31,3 +31,37 @@ public protocol ConformanceMacro: AttachedMacro {
3131
in context: some MacroExpansionContext
3232
) throws -> [(TypeSyntax, GenericWhereClauseSyntax?)]
3333
}
34+
35+
extension ConformanceMacro {
36+
public static func expansion(
37+
of node: AttributeSyntax,
38+
attachedTo declaration: some DeclGroupSyntax,
39+
providingExtensionsOf type: some TypeSyntaxProtocol,
40+
in context: some MacroExpansionContext
41+
) throws -> [ExtensionDeclSyntax] {
42+
43+
let newConformances = try expansion(
44+
of: node,
45+
providingConformancesOf: declaration,
46+
in: context
47+
)
48+
49+
var extensions: [ExtensionDeclSyntax] = []
50+
for (proto, whereClause) in newConformances {
51+
let decl: DeclSyntax =
52+
"""
53+
extension \(type.trimmed): \(proto) {}
54+
"""
55+
56+
var extensionDecl = decl.cast(ExtensionDeclSyntax.self)
57+
58+
if let whereClause {
59+
extensionDecl = extensionDecl.with(\.genericWhereClause, whereClause)
60+
}
61+
62+
extensions.append(extensionDecl)
63+
}
64+
65+
return extensions
66+
}
67+
}

Sources/SwiftSyntaxMacros/MacroSystem.swift

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
115115
|| macro is MemberMacro.Type
116116
|| macro is AccessorMacro.Type
117117
|| macro is MemberAttributeMacro.Type
118-
|| macro is ConformanceMacro.Type
119118
|| macro is ExtensionMacro.Type)
120119
}
121120

@@ -192,7 +191,7 @@ class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
192191

193192
if let declGroup = decl.asProtocol(DeclGroupSyntax.self) {
194193
newItems.append(
195-
contentsOf: expandConformances(of: declGroup).map {
194+
contentsOf: expandExtensions(of: declGroup).map {
196195
newDecl in CodeBlockItemSyntax(item: .decl(newDecl))
197196
}
198197
)
@@ -408,45 +407,25 @@ extension MacroApplication {
408407
// If any of the custom attributes associated with the given declaration
409408
// refer to conformance macros, expand them and return the resulting
410409
// set of extension declarations.
411-
private func expandConformances(of decl: DeclGroupSyntax) -> [DeclSyntax] {
412-
let extendedType: Syntax
410+
private func expandExtensions(of decl: DeclGroupSyntax) -> [DeclSyntax] {
411+
let extendedType: TypeSyntax
413412
if let identified = decl.asProtocol(IdentifiedDeclSyntax.self) {
414-
extendedType = Syntax(identified.identifier.trimmed)
413+
extendedType = "\(identified.identifier.trimmed)"
415414
} else if let ext = decl.as(ExtensionDeclSyntax.self) {
416-
extendedType = Syntax(ext.extendedType.trimmed)
415+
extendedType = "\(ext.extendedType.trimmed)"
417416
} else {
418417
return []
419418
}
420419

421420
var extensions: [DeclSyntax] = []
422-
let macroAttributes = getMacroAttributes(attachedTo: decl.as(DeclSyntax.self)!, ofType: ConformanceMacro.Type.self)
423-
for (attribute, conformanceMacro) in macroAttributes {
424-
do {
425-
let newConformances = try conformanceMacro.expansion(of: attribute, providingConformancesOf: decl, in: context)
426-
427-
for (type, whereClause) in newConformances {
428-
var ext: DeclSyntax = """
429-
extension \(extendedType): \(type) { }
430-
"""
431-
if let whereClause {
432-
ext = DeclSyntax((ext.cast(ExtensionDeclSyntax.self)).with(\.genericWhereClause, whereClause))
433-
}
434-
435-
extensions.append(DeclSyntax(ext))
436-
}
437-
} catch {
438-
context.addDiagnostics(from: error, node: attribute)
439-
}
440-
}
441421

442422
let extensionMacroAttrs = getMacroAttributes(attachedTo: decl.as(DeclSyntax.self)!, ofType: ExtensionMacro.Type.self)
443-
let extendedTypeSyntax = TypeSyntax("\(extendedType.trimmed)")
444423
for (attribute, extensionMacro) in extensionMacroAttrs {
445424
do {
446425
let newExtensions = try extensionMacro.expansion(
447426
of: attribute,
448427
attachedTo: decl,
449-
providingExtensionsOf: extendedTypeSyntax,
428+
providingExtensionsOf: extendedType,
450429
in: context
451430
)
452431

0 commit comments

Comments
 (0)