Skip to content

Commit 0daf4a1

Browse files
[CodeGenLib] Add avialability gurads in generated code (#1791)
Motivation: The protocols from GRPCCore that structs and protocols from the generated code conform to can be used only for some OSes, so we need to add the same availability guards in the generated code. Modifications: - created the AvailabilityDescription struct in StructuredSwiftRepresentation - created the declaraion for a guarded declaration - created the rendering functions for guarded declarations and the availability guard - added the guarded declaration in the translators where needed - changed the tests accordingly Result: The generated code will contain the necessary availability guards.
1 parent 2fa9d9a commit 0daf4a1

File tree

10 files changed

+133
-8
lines changed

10 files changed

+133
-8
lines changed

Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,8 @@ struct TextBasedRenderer: RendererProtocol {
818818
renderCommentableDeclaration(comment: comment, declaration: nestedDeclaration)
819819
case let .deprecated(deprecation, nestedDeclaration):
820820
renderDeprecatedDeclaration(deprecation: deprecation, declaration: nestedDeclaration)
821+
case let .guarded(availability, nestedDeclaration):
822+
renderGuardedDeclaration(availability: availability, declaration: nestedDeclaration)
821823
case .variable(let variableDescription): renderVariable(variableDescription)
822824
case .extension(let extensionDescription): renderExtension(extensionDescription)
823825
case .struct(let structDescription): renderStruct(structDescription)
@@ -1074,6 +1076,21 @@ struct TextBasedRenderer: RendererProtocol {
10741076
writer.writeLine(line)
10751077
}
10761078

1079+
/// Renders the specified declaration with an availability guard annotation.
1080+
func renderGuardedDeclaration(availability: AvailabilityDescription, declaration: Declaration) {
1081+
renderAvailability(availability)
1082+
renderDeclaration(declaration)
1083+
}
1084+
1085+
func renderAvailability(_ availability: AvailabilityDescription) {
1086+
var line = "@available("
1087+
for osVersion in availability.osVersions {
1088+
line.append("\(osVersion.os.name) \(osVersion.version), ")
1089+
}
1090+
line.append("*)")
1091+
writer.writeLine(line)
1092+
}
1093+
10771094
/// Renders the specified code block item.
10781095
func renderCodeBlockItem(_ description: CodeBlockItem) {
10791096
switch description {

Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,9 @@ indirect enum Declaration: Equatable, Codable {
764764
/// A declaration that adds a comment on top of the provided declaration.
765765
case deprecated(DeprecationDescription, Declaration)
766766

767+
/// A declaration that adds an availability guard on top of the provided declaration.
768+
case guarded(AvailabilityDescription, Declaration)
769+
767770
/// A variable declaration.
768771
case variable(VariableDescription)
769772

@@ -801,6 +804,42 @@ struct DeprecationDescription: Equatable, Codable {
801804
var renamed: String?
802805
}
803806

807+
/// A description of an availability guard.
808+
///
809+
/// For example: `@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)`
810+
struct AvailabilityDescription: Equatable, Codable {
811+
/// The array of OSes and versions which are specified in the availability guard.
812+
var osVersions: [OSVersion]
813+
init(osVersions: [OSVersion]) {
814+
self.osVersions = osVersions
815+
}
816+
817+
/// An OS and its version.
818+
struct OSVersion: Equatable, Codable {
819+
var os: OS
820+
var version: String
821+
init(os: OS, version: String) {
822+
self.os = os
823+
self.version = version
824+
}
825+
}
826+
827+
/// One of the possible OSes.
828+
// swift-format-ignore: DontRepeatTypeInStaticProperties
829+
struct OS: Equatable, Codable {
830+
var name: String
831+
832+
init(name: String) {
833+
self.name = name
834+
}
835+
836+
static let macOS = Self(name: "macOS")
837+
static let iOS = Self(name: "iOS")
838+
static let watchOS = Self(name: "watchOS")
839+
static let tvOS = Self(name: "tvOS")
840+
}
841+
}
842+
804843
/// A description of an assignment expression.
805844
///
806845
/// For example: `foo = 42`.
@@ -1803,6 +1842,7 @@ extension Declaration {
18031842
switch self {
18041843
case .commentable(_, let declaration): return declaration.accessModifier
18051844
case .deprecated(_, let declaration): return declaration.accessModifier
1845+
case .guarded(_, let declaration): return declaration.accessModifier
18061846
case .variable(let variableDescription): return variableDescription.accessModifier
18071847
case .extension(let extensionDescription): return extensionDescription.accessModifier
18081848
case .struct(let structDescription): return structDescription.accessModifier
@@ -1821,6 +1861,9 @@ extension Declaration {
18211861
case .deprecated(let deprecationDescription, var declaration):
18221862
declaration.accessModifier = newValue
18231863
self = .deprecated(deprecationDescription, declaration)
1864+
case .guarded(let availability, var declaration):
1865+
declaration.accessModifier = newValue
1866+
self = .guarded(availability, declaration)
18241867
case .variable(var variableDescription):
18251868
variableDescription.accessModifier = newValue
18261869
self = .variable(variableDescription)

Sources/GRPCCodeGen/Internal/Translator/ClientCodeTranslator.swift

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -332,12 +332,15 @@ extension ClientCodeTranslator {
332332
)
333333
}
334334

335-
return .struct(
336-
StructDescription(
337-
accessModifier: self.accessModifier,
338-
name: "\(service.namespacedGeneratedName)Client",
339-
conformances: ["\(service.namespacedTypealiasGeneratedName).ClientProtocol"],
340-
members: [clientProperty, initializer] + methods
335+
return .guarded(
336+
self.availabilityGuard,
337+
.struct(
338+
StructDescription(
339+
accessModifier: self.accessModifier,
340+
name: "\(service.namespacedGeneratedName)Client",
341+
conformances: ["\(service.namespacedTypealiasGeneratedName).ClientProtocol"],
342+
members: [clientProperty, initializer] + methods
343+
)
341344
)
342345
)
343346
}

Sources/GRPCCodeGen/Internal/Translator/ServerCodeTranslator.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,10 @@ extension ServerCodeTranslator {
125125
)
126126
)
127127

128-
return .commentable(.preFormatted(service.documentation), streamingProtocol)
128+
return .commentable(
129+
.preFormatted(service.documentation),
130+
.guarded(self.availabilityGuard, streamingProtocol)
131+
)
129132
}
130133

131134
private func makeStreamingMethodSignature(
@@ -188,7 +191,10 @@ extension ServerCodeTranslator {
188191
]
189192
)
190193
let registerRPCsBody = self.makeRegisterRPCsMethodBody(for: service, in: codeGenerationRequest)
191-
return .function(signature: registerRPCsSignature, body: registerRPCsBody)
194+
return .guarded(
195+
self.availabilityGuard,
196+
.function(signature: registerRPCsSignature, body: registerRPCsBody)
197+
)
192198
}
193199

194200
private func makeRegisterRPCsMethodBody(

Sources/GRPCCodeGen/Internal/Translator/SpecializedTranslator.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,13 @@ extension SpecializedTranslator {
4646
}
4747
}
4848
}
49+
50+
internal var availabilityGuard: AvailabilityDescription {
51+
AvailabilityDescription(osVersions: [
52+
.init(os: .macOS, version: "13.0"),
53+
.init(os: .iOS, version: "16.0"),
54+
.init(os: .watchOS, version: "9.0"),
55+
.init(os: .tvOS, version: "16.0"),
56+
])
57+
}
4958
}

Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,21 @@ final class Test_TextBasedRenderer: XCTestCase {
667667
)
668668
}
669669
670+
func testAvailability() throws {
671+
try _test(
672+
.init(osVersions: [
673+
.init(os: .macOS, version: "12.0"),
674+
.init(os: .iOS, version: "13.1.2"),
675+
.init(os: .watchOS, version: "8.1.2"),
676+
.init(os: .tvOS, version: "15.0.2"),
677+
]),
678+
renderedBy: TextBasedRenderer.renderAvailability,
679+
rendersAs: #"""
680+
@available(macOS 12.0, iOS 13.1.2, watchOS 8.1.2, tvOS 15.0.2, *)
681+
"""#
682+
)
683+
}
684+
670685
func testBindingKind() throws {
671686
try _test(
672687
.var,

Tests/GRPCCodeGenTests/Internal/Translator/ClientCodeTranslatorSnippetBasedTests.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
6666
}
6767
}
6868
/// Documentation for ServiceA
69+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
6970
public struct NamespaceA_ServiceAClient: NamespaceA.ServiceA.ClientProtocol {
7071
private let client: GRPCCore.GRPCClient
7172
@@ -139,6 +140,7 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
139140
}
140141
}
141142
/// Documentation for ServiceA
143+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
142144
public struct NamespaceA_ServiceAClient: NamespaceA.ServiceA.ClientProtocol {
143145
private let client: GRPCCore.GRPCClient
144146
@@ -212,6 +214,7 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
212214
}
213215
}
214216
/// Documentation for ServiceA
217+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
215218
public struct NamespaceA_ServiceAClient: NamespaceA.ServiceA.ClientProtocol {
216219
private let client: GRPCCore.GRPCClient
217220
@@ -285,6 +288,7 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
285288
}
286289
}
287290
/// Documentation for ServiceA
291+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
288292
public struct NamespaceA_ServiceAClient: NamespaceA.ServiceA.ClientProtocol {
289293
private let client: GRPCCore.GRPCClient
290294
@@ -386,6 +390,7 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
386390
}
387391
}
388392
/// Documentation for ServiceA
393+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
389394
package struct NamespaceA_ServiceAClient: NamespaceA.ServiceA.ClientProtocol {
390395
private let client: GRPCCore.GRPCClient
391396
@@ -475,6 +480,7 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
475480
}
476481
}
477482
/// Documentation for ServiceA
483+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
478484
internal struct ServiceAClient: ServiceA.ClientProtocol {
479485
private let client: GRPCCore.GRPCClient
480486
@@ -535,6 +541,7 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
535541
extension NamespaceA.ServiceA.ClientProtocol {
536542
}
537543
/// Documentation for ServiceA
544+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
538545
public struct NamespaceA_ServiceAClient: NamespaceA.ServiceA.ClientProtocol {
539546
private let client: GRPCCore.GRPCClient
540547
@@ -551,6 +558,7 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
551558
/// Documentation for ServiceB
552559
///
553560
/// Line 2
561+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
554562
public struct ServiceBClient: ServiceB.ClientProtocol {
555563
private let client: GRPCCore.GRPCClient
556564

Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,12 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase {
181181
}
182182
183183
/// Documentation for AService
184+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
184185
public protocol NamespaceA_ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {}
185186
186187
/// Conformance to `GRPCCore.RegistrableRPCService`.
187188
extension NamespaceA.ServiceA.StreamingServiceProtocol {
189+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
188190
public func registerMethods(with router: inout GRPCCore.RPCRouter) {}
189191
}
190192

Tests/GRPCCodeGenTests/Internal/Translator/ServerCodeTranslatorSnippetBasedTests.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,14 @@ final class ServerCodeTranslatorSnippetBasedTests: XCTestCase {
5151
let expectedSwift =
5252
"""
5353
/// Documentation for ServiceA
54+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
5455
public protocol NamespaceA_ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
5556
/// Documentation for unaryMethod
5657
func unary(request: ServerRequest.Stream<NamespaceA.ServiceA.Method.Unary.Input>) async throws -> ServerResponse.Stream<NamespaceA.ServiceA.Method.Unary.Output>
5758
}
5859
/// Conformance to `GRPCCore.RegistrableRPCService`.
5960
extension NamespaceA.ServiceA.StreamingServiceProtocol {
61+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
6062
public func registerMethods(with router: inout GRPCCore.RPCRouter) {
6163
router.registerHandler(
6264
forMethod: NamespaceA.ServiceA.Method.Unary.descriptor,
@@ -115,12 +117,14 @@ final class ServerCodeTranslatorSnippetBasedTests: XCTestCase {
115117
let expectedSwift =
116118
"""
117119
/// Documentation for ServiceA
120+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
118121
package protocol NamespaceA_ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
119122
/// Documentation for inputStreamingMethod
120123
func inputStreaming(request: ServerRequest.Stream<NamespaceA.ServiceA.Method.InputStreaming.Input>) async throws -> ServerResponse.Stream<NamespaceA.ServiceA.Method.InputStreaming.Output>
121124
}
122125
/// Conformance to `GRPCCore.RegistrableRPCService`.
123126
extension NamespaceA.ServiceA.StreamingServiceProtocol {
127+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
124128
package func registerMethods(with router: inout GRPCCore.RPCRouter) {
125129
router.registerHandler(
126130
forMethod: NamespaceA.ServiceA.Method.InputStreaming.descriptor,
@@ -183,12 +187,14 @@ final class ServerCodeTranslatorSnippetBasedTests: XCTestCase {
183187
let expectedSwift =
184188
"""
185189
/// Documentation for ServiceA
190+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
186191
public protocol NamespaceA_ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
187192
/// Documentation for outputStreamingMethod
188193
func outputStreaming(request: ServerRequest.Stream<NamespaceA.ServiceA.Method.OutputStreaming.Input>) async throws -> ServerResponse.Stream<NamespaceA.ServiceA.Method.OutputStreaming.Output>
189194
}
190195
/// Conformance to `GRPCCore.RegistrableRPCService`.
191196
extension NamespaceA.ServiceA.StreamingServiceProtocol {
197+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
192198
public func registerMethods(with router: inout GRPCCore.RPCRouter) {
193199
router.registerHandler(
194200
forMethod: NamespaceA.ServiceA.Method.OutputStreaming.descriptor,
@@ -251,12 +257,14 @@ final class ServerCodeTranslatorSnippetBasedTests: XCTestCase {
251257
let expectedSwift =
252258
"""
253259
/// Documentation for ServiceA
260+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
254261
package protocol NamespaceA_ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
255262
/// Documentation for bidirectionalStreamingMethod
256263
func bidirectionalStreaming(request: ServerRequest.Stream<NamespaceA.ServiceA.Method.BidirectionalStreaming.Input>) async throws -> ServerResponse.Stream<NamespaceA.ServiceA.Method.BidirectionalStreaming.Output>
257264
}
258265
/// Conformance to `GRPCCore.RegistrableRPCService`.
259266
extension NamespaceA.ServiceA.StreamingServiceProtocol {
267+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
260268
package func registerMethods(with router: inout GRPCCore.RPCRouter) {
261269
router.registerHandler(
262270
forMethod: NamespaceA.ServiceA.Method.BidirectionalStreaming.descriptor,
@@ -327,6 +335,7 @@ final class ServerCodeTranslatorSnippetBasedTests: XCTestCase {
327335
let expectedSwift =
328336
"""
329337
/// Documentation for ServiceA
338+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
330339
internal protocol NamespaceA_ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
331340
/// Documentation for inputStreamingMethod
332341
func inputStreaming(request: ServerRequest.Stream<NamespaceA.ServiceA.Method.InputStreaming.Input>) async throws -> ServerResponse.Stream<NamespaceA.ServiceA.Method.InputStreaming.Output>
@@ -336,6 +345,7 @@ final class ServerCodeTranslatorSnippetBasedTests: XCTestCase {
336345
}
337346
/// Conformance to `GRPCCore.RegistrableRPCService`.
338347
extension NamespaceA.ServiceA.StreamingServiceProtocol {
348+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
339349
internal func registerMethods(with router: inout GRPCCore.RPCRouter) {
340350
router.registerHandler(
341351
forMethod: NamespaceA.ServiceA.Method.InputStreaming.descriptor,
@@ -406,12 +416,14 @@ final class ServerCodeTranslatorSnippetBasedTests: XCTestCase {
406416
let expectedSwift =
407417
"""
408418
/// Documentation for ServiceA
419+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
409420
internal protocol ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
410421
/// Documentation for MethodA
411422
func methodA(request: ServerRequest.Stream<ServiceA.Method.MethodA.Input>) async throws -> ServerResponse.Stream<ServiceA.Method.MethodA.Output>
412423
}
413424
/// Conformance to `GRPCCore.RegistrableRPCService`.
414425
extension ServiceA.StreamingServiceProtocol {
426+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
415427
internal func registerMethods(with router: inout GRPCCore.RPCRouter) {
416428
router.registerHandler(
417429
forMethod: ServiceA.Method.MethodA.descriptor,
@@ -468,9 +480,11 @@ final class ServerCodeTranslatorSnippetBasedTests: XCTestCase {
468480
let expectedSwift =
469481
"""
470482
/// Documentation for ServiceA
483+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
471484
public protocol NamespaceA_ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {}
472485
/// Conformance to `GRPCCore.RegistrableRPCService`.
473486
extension NamespaceA.ServiceA.StreamingServiceProtocol {
487+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
474488
public func registerMethods(with router: inout GRPCCore.RPCRouter) {}
475489
}
476490
/// Documentation for ServiceA
@@ -479,9 +493,11 @@ final class ServerCodeTranslatorSnippetBasedTests: XCTestCase {
479493
extension NamespaceA.ServiceA.ServiceProtocol {
480494
}
481495
/// Documentation for ServiceB
496+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
482497
public protocol NamespaceA_ServiceBStreamingServiceProtocol: GRPCCore.RegistrableRPCService {}
483498
/// Conformance to `GRPCCore.RegistrableRPCService`.
484499
extension NamespaceA.ServiceB.StreamingServiceProtocol {
500+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
485501
public func registerMethods(with router: inout GRPCCore.RPCRouter) {}
486502
}
487503
/// Documentation for ServiceB

0 commit comments

Comments
 (0)