Skip to content

Commit 10b648d

Browse files
authored
Only import SwiftProtobuf when WKTs are used as input/output (#86)
Modification: The code generator imports SwiftProtobuf if the file being generated has a dependency on any of the protos bundled by SwiftProtobuf. This can yield unnecessary imports which may warn if used in conjunction with access levels on imports. The problem is that the grpc generator checks for dependencies on the FileDescriptor which may include messages defined in the same file as a service. If any of those messages depend on a well known type (WKT) then an import will be added even though the code being generated doesn't include message code. Modifications: - Only include a SwiftProtobuf import if an input or output type for an RPC is a bundled proto. - Fix a test where a WKT is used in a message but not as an input/output type for an RPC. Result: Fewer warnings
1 parent 1a75da3 commit 10b648d

File tree

6 files changed

+69
-44
lines changed

6 files changed

+69
-44
lines changed

Sources/GRPCProtobufCodeGen/ProtobufCodeGenParser.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,26 @@ extension ProtobufCodeGenParser {
110110
var codeDependencies: [Dependency] = [
111111
Dependency(module: self.moduleNames.grpcProtobuf, accessLevel: .internal)
112112
]
113+
113114
// If there's a dependency on a bundled proto then add the SwiftProtobuf import.
114115
//
115116
// Importing SwiftProtobuf unconditionally results in warnings in the generated
116117
// code if access-levels are used on imports and no bundled protos are used.
117-
let dependsOnBundledProto = file.dependencies.contains { descriptor in
118-
SwiftProtobufInfo.isBundledProto(file: descriptor)
118+
//
119+
// The import is only needed if a bundled proto is used as the input or output type
120+
// for an RPC. The file may have a dependency on a bundled proto without requiring
121+
// the SwiftProtobuf import (e.g. a message depending on a bundle proto may be
122+
// defined in the same file as the service, the dependency will be in the '.pb.swift'
123+
// along with the message code).
124+
let needsProtobufImport = file.services.contains { service in
125+
service.methods.contains { method in
126+
let inputIsBundled = SwiftProtobufInfo.isBundledProto(file: method.inputType.file)
127+
let outputIsBundled = SwiftProtobufInfo.isBundledProto(file: method.outputType.file)
128+
return inputIsBundled || outputIsBundled
129+
}
119130
}
120131

121-
if dependsOnBundledProto {
132+
if needsProtobufImport {
122133
let dependency = Dependency(
123134
module: self.moduleNames.swiftProtobuf,
124135
accessLevel: self.accessLevel

Sources/protoc-gen-grpc-swift-2/Options.swift

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ struct GeneratorOptions {
6262
}
6363

6464
init(pairs: [(key: String, value: String)]) throws {
65+
var moduleMapPath: String?
66+
6567
for pair in pairs {
6668
switch pair.key {
6769
case "Visibility":
@@ -87,14 +89,7 @@ struct GeneratorOptions {
8789

8890
case "ProtoPathModuleMappings":
8991
if !pair.value.isEmpty {
90-
do {
91-
self.protoToModuleMappings = try ProtoFileToModuleMappings(path: pair.value)
92-
} catch let e {
93-
throw GenerationError.wrappedError(
94-
message: "Parameter 'ProtoPathModuleMappings=\(pair.value)'",
95-
error: e
96-
)
97-
}
92+
moduleMapPath = pair.value
9893
}
9994

10095
case "FileNaming":
@@ -164,6 +159,24 @@ struct GeneratorOptions {
164159
throw GenerationError.unknownParameter(name: pair.key)
165160
}
166161
}
162+
163+
if let moduleMapPath = moduleMapPath {
164+
do {
165+
self.protoToModuleMappings = try ProtoFileToModuleMappings(
166+
path: moduleMapPath,
167+
swiftProtobufModuleName: self.config.moduleNames.swiftProtobuf
168+
)
169+
} catch let e {
170+
throw GenerationError.wrappedError(
171+
message: "Parameter 'ProtoPathModuleMappings=\(moduleMapPath)'",
172+
error: e
173+
)
174+
}
175+
} else {
176+
self.protoToModuleMappings = ProtoFileToModuleMappings(
177+
swiftProtobufModuleName: self.config.moduleNames.swiftProtobuf
178+
)
179+
}
167180
}
168181

169182
static func parseParameter(string: String?) -> [(key: String, value: String)] {
-5 Bytes
Binary file not shown.

Tests/GRPCProtobufCodeGenTests/ProtobufCodeGenParserTests.swift

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ struct ProtobufCodeGenParserTests {
6666
@available(gRPCSwiftProtobuf 2.0, *)
6767
func dependencies() throws {
6868
let expected: [GRPCCodeGen.Dependency] = [
69-
.init(module: "GRPCProtobuf", accessLevel: .internal), // Always an internal import
70-
.init(module: "SwiftProtobuf", accessLevel: .internal),
69+
.init(module: "GRPCProtobuf", accessLevel: .internal) // Always an internal import
7170
]
7271
#expect(try self.codeGen.dependencies == expected)
7372
}
@@ -266,5 +265,37 @@ struct ProtobufCodeGenParserTests {
266265
]
267266
#expect(codeGen.dependencies == expected)
268267
}
268+
269+
@Test("Generate with different module names")
270+
@available(gRPCSwiftProtobuf 2.0, *)
271+
func generateWithDifferentModuleNames() throws {
272+
var config = ProtobufCodeGenerator.Config.defaults
273+
let defaultNames = config.moduleNames
274+
275+
config.accessLevel = .public
276+
config.indentation = 2
277+
config.moduleNames.grpcCore = String(config.moduleNames.grpcCore.reversed())
278+
config.moduleNames.grpcProtobuf = String(config.moduleNames.grpcProtobuf.reversed())
279+
config.moduleNames.swiftProtobuf = String(config.moduleNames.swiftProtobuf.reversed())
280+
281+
let generator = ProtobufCodeGenerator(config: config)
282+
let generated = try generator.generateCode(
283+
fileDescriptor: Self.fileDescriptor,
284+
protoFileModuleMappings: ProtoFileToModuleMappings(
285+
swiftProtobufModuleName: config.moduleNames.swiftProtobuf
286+
),
287+
extraModuleImports: []
288+
)
289+
290+
// Mustn't contain the default names.
291+
#expect(!generated.contains(defaultNames.grpcCore))
292+
#expect(!generated.contains(defaultNames.grpcProtobuf))
293+
#expect(!generated.contains(defaultNames.swiftProtobuf))
294+
295+
// Must contain the configured names.
296+
#expect(generated.contains(config.moduleNames.grpcCore))
297+
#expect(generated.contains(config.moduleNames.grpcProtobuf))
298+
#expect(generated.contains(config.moduleNames.swiftProtobuf))
299+
}
269300
}
270301
}

Tests/GRPCProtobufCodeGenTests/ProtobufCodeGeneratorTests.swift

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ struct ProtobufCodeGeneratorTests {
9999
100100
import GRPCCore
101101
import GRPCProtobuf
102-
import SwiftProtobuf
103102
104103
// MARK: - test.TestService
105104
@@ -1103,35 +1102,6 @@ struct ProtobufCodeGeneratorTests {
11031102
#expect(generated == expected)
11041103
}
11051104

1106-
@Test("Generate with different module names")
1107-
@available(gRPCSwiftProtobuf 2.0, *)
1108-
func generateWithDifferentModuleNames() throws {
1109-
var config = ProtobufCodeGenerator.Config.defaults
1110-
let defaultNames = config.moduleNames
1111-
1112-
config.accessLevel = .public
1113-
config.indentation = 2
1114-
config.moduleNames.grpcCore = String(config.moduleNames.grpcCore.reversed())
1115-
config.moduleNames.grpcProtobuf = String(config.moduleNames.grpcProtobuf.reversed())
1116-
config.moduleNames.swiftProtobuf = String(config.moduleNames.swiftProtobuf.reversed())
1117-
1118-
let generator = ProtobufCodeGenerator(config: config)
1119-
let generated = try generator.generateCode(
1120-
fileDescriptor: Self.fileDescriptor,
1121-
protoFileModuleMappings: ProtoFileToModuleMappings(),
1122-
extraModuleImports: []
1123-
)
1124-
1125-
// Mustn't contain the default names.
1126-
#expect(!generated.contains(defaultNames.grpcCore))
1127-
#expect(!generated.contains(defaultNames.grpcProtobuf))
1128-
#expect(!generated.contains(defaultNames.swiftProtobuf))
1129-
1130-
// Must contain the configured names.
1131-
#expect(generated.contains(config.moduleNames.grpcCore))
1132-
#expect(generated.contains(config.moduleNames.grpcProtobuf))
1133-
#expect(generated.contains(config.moduleNames.swiftProtobuf))
1134-
}
11351105
}
11361106

11371107
@Suite("File-without-services (foo-messages.proto)")

dev/protos/local/test-service.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ syntax = "proto3";
33

44
package test;
55

6-
// Using a WKT forces an "SwiftProtobuf" to be imported in generated code.
6+
// WKT isn't used in API generated by gRPC, so no import is expected.
77
import "google/protobuf/any.proto";
88

99
// Service docs.

0 commit comments

Comments
 (0)