Skip to content

Commit 637f049

Browse files
authored
Merge pull request #1773 from DougGregor/expand-conformance-macros
2 parents b0f9542 + bd24a3f commit 637f049

File tree

2 files changed

+94
-1
lines changed

2 files changed

+94
-1
lines changed

Sources/SwiftSyntaxMacros/MacroSystem.swift

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,14 +178,23 @@ class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
178178
let newItem = visit(item.item)
179179
newItems.append(item.with(\.item, newItem))
180180

181-
// Expand any peer declarations triggered by macros used as attributes.
181+
// Expand any peer declarations or conformances triggered by macros used
182+
// as attributes.
182183
if case let .decl(decl) = item.item {
183184
let peers = expandPeers(of: decl)
184185
newItems.append(
185186
contentsOf: peers.map {
186187
newDecl in CodeBlockItemSyntax(item: .decl(newDecl))
187188
}
188189
)
190+
191+
if let declGroup = decl.asProtocol(DeclGroupSyntax.self) {
192+
newItems.append(
193+
contentsOf: expandConformances(of: declGroup).map {
194+
newDecl in CodeBlockItemSyntax(item: .decl(newDecl))
195+
}
196+
)
197+
}
189198
}
190199
}
191200

@@ -394,6 +403,43 @@ extension MacroApplication {
394403
return peers
395404
}
396405

406+
// If any of the custom attributes associated with the given declaration
407+
// refer to conformance macros, expand them and return the resulting
408+
// set of extension declarations.
409+
private func expandConformances(of decl: DeclGroupSyntax) -> [DeclSyntax] {
410+
let extendedType: Syntax
411+
if let identified = decl.asProtocol(IdentifiedDeclSyntax.self) {
412+
extendedType = Syntax(identified.identifier.trimmed)
413+
} else if let ext = decl.as(ExtensionDeclSyntax.self) {
414+
extendedType = Syntax(ext.extendedType.trimmed)
415+
} else {
416+
return []
417+
}
418+
419+
var extensions: [DeclSyntax] = []
420+
let macroAttributes = getMacroAttributes(attachedTo: decl.as(DeclSyntax.self)!, ofType: ConformanceMacro.Type.self)
421+
for (attribute, conformanceMacro) in macroAttributes {
422+
do {
423+
let newConformances = try conformanceMacro.expansion(of: attribute, providingConformancesOf: decl, in: context)
424+
425+
for (type, whereClause) in newConformances {
426+
var ext: DeclSyntax = """
427+
extension \(extendedType): \(type) { }
428+
"""
429+
if let whereClause {
430+
ext = DeclSyntax((ext.cast(ExtensionDeclSyntax.self)).with(\.genericWhereClause, whereClause))
431+
}
432+
433+
extensions.append(DeclSyntax(ext))
434+
}
435+
} catch {
436+
context.addDiagnostics(from: error, node: attribute)
437+
}
438+
}
439+
440+
return extensions
441+
}
442+
397443
/// Expands any attached custom attributes that refer to member declaration macros,
398444
/// and returns result of adding those members to the given declaration.
399445
private func expandMembers<Decl: DeclGroupSyntax & DeclSyntaxProtocol>(

Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,16 @@ public struct DeclsFromStringsMacro: DeclarationMacro {
672672
}
673673
}
674674

675+
public struct SendableConformanceMacro: ConformanceMacro {
676+
public static func expansion(
677+
of node: AttributeSyntax,
678+
providingConformancesOf declaration: some DeclGroupSyntax,
679+
in context: some MacroExpansionContext
680+
) throws -> [(TypeSyntax, GenericWhereClauseSyntax?)] {
681+
return [("Sendable", nil)]
682+
}
683+
}
684+
675685
public struct DeclsFromStringsMacroNoAttrs: DeclarationMacro {
676686
public static var propagateFreestandingMacroAttributes: Bool { false }
677687
public static var propagateFreestandingMacroModifiers: Bool { false }
@@ -715,6 +725,7 @@ public let testMacros: [String: Macro.Type] = [
715725
"wrapStoredProperties": WrapStoredProperties.self,
716726
"customTypeWrapper": CustomTypeWrapperMacro.self,
717727
"unwrap": UnwrapMacro.self,
728+
"AddSendable": SendableConformanceMacro.self,
718729
]
719730

720731
final class MacroSystemTests: XCTestCase {
@@ -1149,4 +1160,40 @@ final class MacroSystemTests: XCTestCase {
11491160
)
11501161

11511162
}
1163+
1164+
func testConformanceExpansion() {
1165+
assertMacroExpansion(
1166+
"""
1167+
@AddSendable
1168+
struct MyType {
1169+
}
1170+
""",
1171+
expandedSource: """
1172+
1173+
struct MyType {
1174+
}
1175+
extension MyType: Sendable {
1176+
}
1177+
""",
1178+
macros: testMacros,
1179+
indentationWidth: indentationWidth
1180+
)
1181+
1182+
assertMacroExpansion(
1183+
"""
1184+
@AddSendable
1185+
extension A.B {
1186+
}
1187+
""",
1188+
expandedSource: """
1189+
1190+
extension A.B {
1191+
}
1192+
extension A.B: Sendable {
1193+
}
1194+
""",
1195+
macros: testMacros,
1196+
indentationWidth: indentationWidth
1197+
)
1198+
}
11521199
}

0 commit comments

Comments
 (0)