Skip to content

Commit cc1b386

Browse files
committed
[Observation] Forward availability and defines to extensions
1 parent 3748b0f commit cc1b386

File tree

5 files changed

+188
-15
lines changed

5 files changed

+188
-15
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
import SwiftSyntax
13+
import SwiftSyntaxMacros
14+
import SwiftDiagnostics
15+
import SwiftOperators
16+
import SwiftSyntaxBuilder
17+
18+
19+
extension AttributeSyntax {
20+
var availability: AttributeSyntax? {
21+
if attributeName.identifier == "available" {
22+
return self
23+
} else {
24+
return nil
25+
}
26+
}
27+
}
28+
29+
extension IfConfigClauseSyntax.Elements {
30+
var availability: IfConfigClauseSyntax.Elements? {
31+
switch self {
32+
case .attributes(let attributes):
33+
if let availability = attributes.availability {
34+
return .attributes(availability)
35+
} else {
36+
return nil
37+
}
38+
default:
39+
return nil
40+
}
41+
}
42+
}
43+
44+
extension IfConfigClauseSyntax {
45+
var availability: IfConfigClauseSyntax? {
46+
if let availability = elements?.availability {
47+
return IfConfigClauseSyntax(
48+
leadingTrivia: leadingTrivia,
49+
unexpectedBeforePoundKeyword,
50+
poundKeyword: poundKeyword,
51+
unexpectedBetweenPoundKeywordAndCondition,
52+
condition: condition,
53+
unexpectedBetweenConditionAndElements,
54+
elements: availability,
55+
unexpectedAfterElements,
56+
trailingTrivia: trailingTrivia
57+
)
58+
} else {
59+
return nil
60+
}
61+
}
62+
63+
var clonedAsIf: IfConfigClauseSyntax {
64+
IfConfigClauseSyntax(
65+
leadingTrivia: leadingTrivia,
66+
unexpectedBeforePoundKeyword,
67+
poundKeyword: .poundIfKeyword(),
68+
unexpectedBetweenPoundKeywordAndCondition,
69+
condition: condition,
70+
unexpectedBetweenConditionAndElements,
71+
elements: elements,
72+
unexpectedAfterElements,
73+
trailingTrivia: trailingTrivia
74+
)
75+
}
76+
}
77+
78+
extension IfConfigDeclSyntax {
79+
var availability: IfConfigDeclSyntax? {
80+
var elements = [IfConfigClauseListSyntax.Element]()
81+
for clause in clauses {
82+
if let availability = clause.availability {
83+
if elements.isEmpty {
84+
elements.append(availability.clonedAsIf)
85+
} else {
86+
elements.append(availability)
87+
}
88+
}
89+
}
90+
if elements.isEmpty {
91+
return nil
92+
} else {
93+
return IfConfigDeclSyntax(
94+
leadingTrivia: leadingTrivia,
95+
unexpectedBeforeClauses,
96+
clauses: IfConfigClauseListSyntax(elements),
97+
unexpectedBetweenClausesAndPoundEndif,
98+
poundEndif: poundEndif,
99+
unexpectedAfterPoundEndif,
100+
trailingTrivia: trailingTrivia
101+
)
102+
}
103+
104+
}
105+
}
106+
107+
extension AttributeListSyntax.Element {
108+
var availability: AttributeListSyntax.Element? {
109+
switch self {
110+
case .attribute(let attribute):
111+
if let availability = attribute.availability {
112+
return .attribute(availability)
113+
}
114+
case .ifConfigDecl(let ifConfig):
115+
if let availability = ifConfig.availability {
116+
return .ifConfigDecl(availability)
117+
}
118+
default:
119+
break
120+
}
121+
return nil
122+
}
123+
}
124+
125+
extension AttributeListSyntax {
126+
var availability: AttributeListSyntax? {
127+
var elements = [AttributeListSyntax.Element]()
128+
for element in self {
129+
if let availability = element.availability {
130+
elements.append(availability)
131+
}
132+
}
133+
if elements.isEmpty {
134+
return nil
135+
}
136+
return AttributeListSyntax(elements)
137+
}
138+
}
139+
140+
extension DeclGroupSyntax {
141+
var availability: AttributeListSyntax? {
142+
if let attributes {
143+
return attributes.availability
144+
} else {
145+
return nil
146+
}
147+
}
148+
}

lib/Macros/Sources/ObservationMacros/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
#===----------------------------------------------------------------------===#
1212

1313
add_swift_macro_library(ObservationMacros
14-
ObservableMacro.swift
14+
Availability.swift
1515
Extensions.swift
16+
ObservableMacro.swift
1617
SWIFT_DEPENDENCIES
1718
SwiftSyntax::SwiftDiagnostics
1819
SwiftSyntax::SwiftOperators

lib/Macros/Sources/ObservationMacros/Extensions.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@
1111

1212
import SwiftSyntax
1313
import SwiftSyntaxMacros
14-
15-
@_implementationOnly import SwiftDiagnostics
16-
@_implementationOnly import SwiftOperators
17-
@_implementationOnly import SwiftSyntaxBuilder
14+
import SwiftDiagnostics
15+
import SwiftOperators
16+
import SwiftSyntaxBuilder
1817

1918
extension VariableDeclSyntax {
2019
var identifierPattern: IdentifierPatternSyntax? {

lib/Macros/Sources/ObservationMacros/ObservableMacro.swift

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@
1111

1212
import SwiftSyntax
1313
import SwiftSyntaxMacros
14-
15-
@_implementationOnly import SwiftDiagnostics
16-
@_implementationOnly import SwiftOperators
17-
@_implementationOnly import SwiftSyntaxBuilder
14+
import SwiftDiagnostics
15+
import SwiftOperators
16+
import SwiftSyntaxBuilder
1817

1918
public struct ObservableMacro {
2019
static let moduleName = "Observation"
@@ -281,13 +280,29 @@ extension ObservableMacro: ExtensionMacro {
281280
return []
282281
}
283282

284-
let decl: DeclSyntax = """
285-
extension \(raw: type.trimmedDescription): \(raw: qualifiedConformanceName) {}
286-
"""
283+
if let availability = declaration.availability {
284+
let emptyBlock: MemberDeclBlockSyntax = "{ }"
287285

288-
return [
289-
decl.cast(ExtensionDeclSyntax.self)
290-
]
286+
return [
287+
ExtensionDeclSyntax(
288+
leadingTrivia: .newline,
289+
attributes: availability,
290+
extensionKeyword: .keyword(.extension),
291+
extendedType: type,
292+
inheritanceClause: TypeInheritanceClauseSyntax(inheritedTypeCollection: InheritedTypeListSyntax([InheritedTypeSyntax(typeName: observableConformanceType)])),
293+
memberBlock: emptyBlock,
294+
trailingTrivia: .newline
295+
)
296+
]
297+
} else {
298+
let decl: DeclSyntax = """
299+
extension \(raw: type.trimmedDescription): \(raw: qualifiedConformanceName) {}
300+
"""
301+
302+
return [
303+
decl.cast(ExtensionDeclSyntax.self)
304+
]
305+
}
291306
}
292307
}
293308

test/stdlib/Observation/Observable.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,16 @@ class RecursiveOuter {
202202
}
203203
}
204204

205+
@Observable
206+
#if FOO
207+
@available(SwiftStdlib 5.9, *)
208+
#elseif BAR
209+
@available(SwiftStdlib 5.9, *)
210+
#else
211+
#endif
212+
class GuardedAvailability {
213+
}
214+
205215
@main
206216
struct Validator {
207217
@MainActor

0 commit comments

Comments
 (0)