Skip to content

Commit c6caad4

Browse files
committed
Swift ObservableMacro to implement ExtensionMacro
This change switch to implement the new ExtensionMacro protocol, the requirement for which includes information about whether the conformance to the Observable protocol has already been added, either in the declaration or in a superclass to the macro-attributed type. This allows the @observable macro to be applied to subclasses of observable types without redundant-conformance errors.
1 parent 1489eba commit c6caad4

File tree

5 files changed

+22
-26
lines changed

5 files changed

+22
-26
lines changed

lib/Macros/Sources/ObservationMacros/ObservableMacro.swift

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -267,30 +267,25 @@ extension ObservableMacro: MemberAttributeMacro {
267267
}
268268
}
269269

270-
extension ObservableMacro: ConformanceMacro {
271-
public static func expansion<Declaration: DeclGroupSyntax, Context: MacroExpansionContext>(
270+
extension ObservableMacro: ExtensionMacro {
271+
public static func expansion(
272272
of node: AttributeSyntax,
273-
providingConformancesOf declaration: Declaration,
274-
in context: Context
275-
) throws -> [(TypeSyntax, GenericWhereClauseSyntax?)] {
276-
let inheritanceList: InheritedTypeListSyntax?
277-
if let classDecl = declaration.as(ClassDeclSyntax.self) {
278-
inheritanceList = classDecl.inheritanceClause?.inheritedTypeCollection
279-
} else if let structDecl = declaration.as(StructDeclSyntax.self) {
280-
inheritanceList = structDecl.inheritanceClause?.inheritedTypeCollection
281-
} else {
282-
inheritanceList = nil
283-
}
284-
285-
if let inheritanceList {
286-
for inheritance in inheritanceList {
287-
if inheritance.typeName.identifier == ObservableMacro.conformanceName {
288-
return []
289-
}
290-
}
273+
attachedTo declaration: some DeclGroupSyntax,
274+
providingExtensionsOf type: some TypeSyntaxProtocol,
275+
conformingTo protocols: [TypeSyntax],
276+
in context: some MacroExpansionContext
277+
) throws -> [ExtensionDeclSyntax] {
278+
if protocols.isEmpty {
279+
return []
291280
}
292-
293-
return [(ObservableMacro.observableConformanceType, nil)]
281+
282+
let decl: DeclSyntax = """
283+
extension \(raw: type.trimmedDescription): \(raw: qualifiedConformanceName) {}
284+
"""
285+
286+
return [
287+
decl.cast(ExtensionDeclSyntax.self)
288+
]
294289
}
295290
}
296291

stdlib/public/Observation/Sources/Observation/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ add_swift_target_library(swiftObservation ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS
2424
SWIFT_COMPILE_FLAGS
2525
${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS}
2626
"-enable-experimental-feature" "Macros"
27+
"-enable-experimental-feature" "ExtensionMacros"
2728
-Xfrontend -disable-implicit-string-processing-module-import
2829

2930
C_COMPILE_FLAGS

stdlib/public/Observation/Sources/Observation/Observable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public protocol Observable { }
4646
@attached(member, names: named(_$observationRegistrar), named(access), named(withMutation), arbitrary)
4747
#endif
4848
@attached(memberAttribute)
49-
@attached(conformance)
49+
@attached(extension, conformances: Observable)
5050
public macro Observable() =
5151
#externalMacro(module: "ObservationMacros", type: "ObservableMacro")
5252

test/stdlib/Observation/Observable.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// REQUIRES: swift_swift_parser, executable_test
22

3-
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library -enable-experimental-feature InitAccessors -enable-experimental-feature Macros -Xfrontend -plugin-path -Xfrontend %swift-host-lib-dir/plugins)
3+
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library -enable-experimental-feature InitAccessors -enable-experimental-feature Macros -enable-experimental-feature ExtensionMacros -Xfrontend -plugin-path -Xfrontend %swift-host-lib-dir/plugins)
44

55
// Run this test via the swift-plugin-server
6-
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library -enable-experimental-feature InitAccessors -enable-experimental-feature Macros -Xfrontend -external-plugin-path -Xfrontend %swift-host-lib-dir/plugins#%swift-plugin-server)
6+
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library -enable-experimental-feature InitAccessors -enable-experimental-feature Macros -enable-experimental-feature ExtensionMacros -Xfrontend -external-plugin-path -Xfrontend %swift-host-lib-dir/plugins#%swift-plugin-server)
77

88
// Asserts is required for '-enable-experimental-feature InitAccessors'.
99
// REQUIRES: asserts

test/stdlib/Observation/ObservableDidSetWillSet.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// REQUIRES: swift_swift_parser, executable_test
22

3-
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -enable-experimental-feature InitAccessors -enable-experimental-feature Macros -Xfrontend -plugin-path -Xfrontend %swift-host-lib-dir/plugins) | %FileCheck %s
3+
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -enable-experimental-feature InitAccessors -enable-experimental-feature Macros -enable-experimental-feature ExtensionMacros -Xfrontend -plugin-path -Xfrontend %swift-host-lib-dir/plugins) | %FileCheck %s
44

55
// Asserts is required for '-enable-experimental-feature InitAccessors'.
66
// REQUIRES: asserts

0 commit comments

Comments
 (0)