Skip to content

Commit 8eebf8e

Browse files
authored
Merge pull request #1780 from DougGregor/expand-conformance-macros-5.9
2 parents be4a332 + f8fb3e7 commit 8eebf8e

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
@@ -173,14 +173,23 @@ class MacroApplication<Context: MacroExpansionContext>: SyntaxRewriter {
173173
let newItem = visit(item.item)
174174
newItems.append(item.with(\.item, newItem))
175175

176-
// Expand any peer declarations triggered by macros used as attributes.
176+
// Expand any peer declarations or conformances triggered by macros used
177+
// as attributes.
177178
if case let .decl(decl) = item.item {
178179
let peers = expandPeers(of: decl)
179180
newItems.append(
180181
contentsOf: peers.map {
181182
newDecl in CodeBlockItemSyntax(item: .decl(newDecl))
182183
}
183184
)
185+
186+
if let declGroup = decl.asProtocol(DeclGroupSyntax.self) {
187+
newItems.append(
188+
contentsOf: expandConformances(of: declGroup).map {
189+
newDecl in CodeBlockItemSyntax(item: .decl(newDecl))
190+
}
191+
)
192+
}
184193
}
185194
}
186195

@@ -389,6 +398,43 @@ extension MacroApplication {
389398
return peers
390399
}
391400

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