diff --git a/servicetalk-grpc-protoc/gradle/checkstyle/suppressions.xml b/servicetalk-grpc-protoc/gradle/checkstyle/suppressions.xml
index 417dd87b76..018924837f 100644
--- a/servicetalk-grpc-protoc/gradle/checkstyle/suppressions.xml
+++ b/servicetalk-grpc-protoc/gradle/checkstyle/suppressions.xml
@@ -21,4 +21,5 @@
+
diff --git a/servicetalk-grpc-protoc/src/main/java/io/servicetalk/grpc/protoc/FileDescriptor.java b/servicetalk-grpc-protoc/src/main/java/io/servicetalk/grpc/protoc/FileDescriptor.java
index 5295b3147e..9350679810 100644
--- a/servicetalk-grpc-protoc/src/main/java/io/servicetalk/grpc/protoc/FileDescriptor.java
+++ b/servicetalk-grpc-protoc/src/main/java/io/servicetalk/grpc/protoc/FileDescriptor.java
@@ -17,6 +17,7 @@
import com.google.protobuf.DescriptorProtos;
import com.google.protobuf.DescriptorProtos.DescriptorProto;
+import com.google.protobuf.DescriptorProtos.EnumDescriptorProto;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.DescriptorProtos.FileOptions;
import com.google.protobuf.DescriptorProtos.MethodDescriptorProto;
@@ -47,23 +48,24 @@
* A single protoc file for which we will be generating classes
*/
final class FileDescriptor implements GenerationContext {
- private static final String GENERATED_BY_COMMENT = "Generated by ServiceTalk proto compiler";
-
+ private static final String GENERATED_BY_COMMENT = "Generated by ServiceTalk gRPC protoc plugin";
+ /**
+ * Inferred behavior from protobuf-java is that if no suffix is explicitly provided the root file name will have
+ * this suffix. See
+ * java_name_resolver.cc
+ */
+ private static final String OUTER_CLASS_SUFFIX = "OuterClass";
private final FileDescriptorProto protoFile;
- private final String sanitizedProtoFileName;
@Nullable
private final String protoPackageName;
private final boolean deprecated;
private final boolean multipleClassFiles;
- @Nullable
private final String javaPackageName;
- @Nullable
private final String outerClassName;
@Nullable
private final String typeNameSuffix;
private final List serviceClassBuilders;
- @Nullable
- private Set reservedJavaTypeName;
+ private final Set reservedJavaTypeName = new HashSet<>();
/**
* A single protoc file for which we will be generating classes
@@ -74,7 +76,7 @@ final class FileDescriptor implements GenerationContext {
FileDescriptor(final FileDescriptorProto protoFile,
@Nullable final String typeNameSuffix) {
this.protoFile = protoFile;
- sanitizedProtoFileName = sanitizeFileName(protoFile.getName());
+ final String sanitizedProtoFileName = sanitizeFileName(protoFile.getName());
protoPackageName = protoFile.hasPackage() ? protoFile.getPackage() : null;
this.typeNameSuffix = typeNameSuffix;
@@ -82,14 +84,18 @@ final class FileDescriptor implements GenerationContext {
final FileOptions fileOptions = protoFile.getOptions();
deprecated = fileOptions.hasDeprecated() && fileOptions.getDeprecated();
multipleClassFiles = fileOptions.hasJavaMultipleFiles() && fileOptions.getJavaMultipleFiles();
- javaPackageName = fileOptions.hasJavaPackage() ? fileOptions.getJavaPackage() : null;
- outerClassName = fileOptions.hasJavaOuterClassname() ? fileOptions.getJavaOuterClassname() : null;
+ javaPackageName = fileOptions.hasJavaPackage() ? fileOptions.getJavaPackage() :
+ inferJavaPackageName(protoPackageName, sanitizedProtoFileName);
+ outerClassName = fileOptions.hasJavaOuterClassname() ?
+ sanitizeClassName(fileOptions.getJavaOuterClassname()) :
+ inferOuterClassName(sanitizedProtoFileName, protoFile);
} else {
deprecated = false;
multipleClassFiles = false;
- javaPackageName = null;
- outerClassName = null;
+ javaPackageName = inferJavaPackageName(protoPackageName, sanitizedProtoFileName);
+ outerClassName = inferOuterClassName(sanitizedProtoFileName, protoFile);
}
+ reservedJavaTypeName.add(outerClassName);
serviceClassBuilders = new ArrayList<>(protoFile.getServiceCount());
}
@@ -115,26 +121,21 @@ DescriptorProtos.SourceCodeInfo sourceCodeInfo() {
}
private void addMessageTypes(final List messageTypes,
- @Nullable String parentProtoScope,
- @Nullable String parentJavaScope,
+ final @Nullable String parentProtoScope,
+ final String parentJavaScope,
final Map messageTypesMap) {
-
messageTypes.forEach(t -> {
- final String protoTypeName = (parentProtoScope != null ? parentProtoScope : "") + '.' + t.getName();
- final String javaClassName = parentJavaScope + '.' + t.getName();
- messageTypesMap.put(protoTypeName, ClassName.bestGuess(javaClassName));
+ final String protoTypeName = parentProtoScope != null ?
+ (parentProtoScope + '.' + t.getName()) : '.' + t.getName();
+ final ClassName className = ClassName.get(parentJavaScope, t.getName());
+ messageTypesMap.put(protoTypeName, className);
- addMessageTypes(t.getNestedTypeList(), protoTypeName, javaClassName, messageTypesMap);
+ addMessageTypes(t.getNestedTypeList(), protoTypeName, className.canonicalName(), messageTypesMap);
});
}
@Override
public String deconflictJavaTypeName(final String name) {
- if (reservedJavaTypeName == null) {
- reservedJavaTypeName = new HashSet<>();
- reservedJavaTypeName.add(outerJavaClassName());
- }
-
if (reservedJavaTypeName.add(name)) {
return name;
}
@@ -227,13 +228,63 @@ private static void insertSingleFileContent(final String content, String fileNam
}
private String outerJavaClassName() {
- return isNotNullNorEmpty(outerClassName) ? sanitizeClassName(outerClassName) :
- sanitizeClassName(sanitizedProtoFileName);
+ return outerClassName;
}
private String javaPackageName() {
- return isNotNullNorEmpty(javaPackageName) ? javaPackageName :
- isNotNullNorEmpty(protoPackageName) ? protoPackageName : sanitizeClassName(sanitizedProtoFileName);
+ return javaPackageName;
+ }
+
+ private static String inferOuterClassName(String sanitizedProtoFileName, FileDescriptorProto protoFile) {
+ final String sanitizeClassName = sanitizeClassName(sanitizedProtoFileName);
+ return hasConflictingClassName(sanitizeClassName, protoFile) ?
+ sanitizeClassName + OUTER_CLASS_SUFFIX : sanitizeClassName;
+ }
+
+ /**
+ * See java_name_resolver.cc.
+ * @param sanitizedClassName The sanitized classname to check for conflicts.
+ * @param protoFile The {@link FileDescriptorProto} to search for conflicting names in.
+ * @return {@code true} if there is a name conflict, {@code false} otherwise.
+ */
+ private static boolean hasConflictingClassName(String sanitizedClassName, FileDescriptorProto protoFile) {
+ for (EnumDescriptorProto enumDescriptor : protoFile.getEnumTypeList()) {
+ if (enumDescriptor.getName().equals(sanitizedClassName)) {
+ return true;
+ }
+ }
+ for (ServiceDescriptorProto serviceDescriptor : protoFile.getServiceList()) {
+ if (serviceDescriptor.getName().equals(sanitizedClassName)) {
+ return true;
+ }
+ }
+ for (DescriptorProto typeDescriptor : protoFile.getMessageTypeList()) {
+ if (hasConflictingClassName(sanitizedClassName, typeDescriptor)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasConflictingClassName(String sanitizedClassName, DescriptorProto typeDescriptor) {
+ if (typeDescriptor.getName().equals(sanitizedClassName)) {
+ return true;
+ }
+ for (DescriptorProto nestedTypeDescriptor : typeDescriptor.getNestedTypeList()) {
+ if (hasConflictingClassName(sanitizedClassName, nestedTypeDescriptor)) {
+ return true;
+ }
+ }
+ for (EnumDescriptorProto enumDescriptor : typeDescriptor.getEnumTypeList()) {
+ if (enumDescriptor.getName().equals(sanitizedClassName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static String inferJavaPackageName(@Nullable String protoPackageName, String sanitizedProtoFileName) {
+ return isNotNullNorEmpty(protoPackageName) ? protoPackageName : sanitizeClassName(sanitizedProtoFileName);
}
private static String sanitizeFileName(final String v) {
diff --git a/servicetalk-grpc-protoc/src/main/java/io/servicetalk/grpc/protoc/Types.java b/servicetalk-grpc-protoc/src/main/java/io/servicetalk/grpc/protoc/Types.java
index 78651df314..6feaebe0f4 100644
--- a/servicetalk-grpc-protoc/src/main/java/io/servicetalk/grpc/protoc/Types.java
+++ b/servicetalk-grpc-protoc/src/main/java/io/servicetalk/grpc/protoc/Types.java
@@ -44,78 +44,80 @@ final class Types {
private static final ClassName Collection = ClassName.get("java.util", "Collection");
static final ClassName RouteExecutionStrategy =
- bestGuess(routerApiPkg + ".RouteExecutionStrategy");
+ ClassName.get(routerApiPkg, "RouteExecutionStrategy");
static final ClassName RouteExecutionStrategyFactory =
- bestGuess(routerApiPkg + ".RouteExecutionStrategyFactory");
+ ClassName.get(routerApiPkg, "RouteExecutionStrategyFactory");
- static final ClassName BlockingIterable = bestGuess(concurrentPkg + ".BlockingIterable");
+ static final ClassName BlockingIterable = ClassName.get(concurrentPkg, "BlockingIterable");
- static final ClassName AsyncCloseable = bestGuess(concurrentApiPkg + ".AsyncCloseable");
- static final ClassName Completable = bestGuess(concurrentApiPkg + ".Completable");
- static final ClassName Publisher = bestGuess(concurrentApiPkg + ".Publisher");
- static final ClassName Single = bestGuess(concurrentApiPkg + ".Single");
+ static final ClassName AsyncCloseable = ClassName.get(concurrentApiPkg, "AsyncCloseable");
+ static final ClassName Completable = ClassName.get(concurrentApiPkg, "Completable");
+ static final ClassName Publisher = ClassName.get(concurrentApiPkg, "Publisher");
+ static final ClassName Single = ClassName.get(concurrentApiPkg, "Single");
- static final ClassName BlockingGrpcClient = bestGuess(grpcApiPkg + ".BlockingGrpcClient");
- static final ClassName BlockingGrpcService = bestGuess(grpcApiPkg + ".BlockingGrpcService");
- static final ClassName GrpcClientMetadata = bestGuess(grpcApiPkg + ".GrpcClientMetadata");
- static final ClassName DefaultGrpcClientMetadata = bestGuess(grpcApiPkg + ".DefaultGrpcClientMetadata");
- static final ClassName GrpcClient = bestGuess(grpcApiPkg + ".GrpcClient");
- static final ClassName GrpcClientCallFactory = bestGuess(grpcApiPkg + ".GrpcClientCallFactory");
- static final ClassName GrpcClientFactory = bestGuess(grpcApiPkg + ".GrpcClientFactory");
- static final ClassName GrpcExecutionContext = bestGuess(grpcApiPkg + ".GrpcExecutionContext");
- static final ClassName GrpcExecutionStrategy = bestGuess(grpcApiPkg + ".GrpcExecutionStrategy");
- static final ClassName GrpcStatusException = bestGuess(grpcApiPkg + ".GrpcStatusException");
- static final ClassName Identity = bestGuess(encodingApiPkg + ".Identity");
- static final ClassName BufferDecoderGroup = bestGuess(encodingApiPkg + ".BufferDecoderGroup");
- static final ClassName EmptyBufferDecoderGroup = bestGuess(encodingApiPkg + ".EmptyBufferDecoderGroup");
- static final ClassName BufferEncoder = bestGuess(encodingApiPkg + ".BufferEncoder");
+ static final ClassName BlockingGrpcClient = ClassName.get(grpcApiPkg, "BlockingGrpcClient");
+ static final ClassName BlockingGrpcService = ClassName.get(grpcApiPkg, "BlockingGrpcService");
+ static final ClassName GrpcClientMetadata = ClassName.get(grpcApiPkg, "GrpcClientMetadata");
+ static final ClassName DefaultGrpcClientMetadata = ClassName.get(grpcApiPkg, "DefaultGrpcClientMetadata");
+ static final ClassName GrpcClient = ClassName.get(grpcApiPkg, "GrpcClient");
+ static final ClassName GrpcClientCallFactory = ClassName.get(grpcApiPkg, "GrpcClientCallFactory");
+ static final ClassName GrpcClientFactory = ClassName.get(grpcApiPkg, "GrpcClientFactory");
+ static final ClassName GrpcExecutionContext = ClassName.get(grpcApiPkg, "GrpcExecutionContext");
+ static final ClassName GrpcExecutionStrategy = ClassName.get(grpcApiPkg, "GrpcExecutionStrategy");
+ static final ClassName GrpcStatusException = ClassName.get(grpcApiPkg, "GrpcStatusException");
+ static final ClassName Identity = ClassName.get(encodingApiPkg, "Identity");
+ static final ClassName BufferDecoderGroup = ClassName.get(encodingApiPkg, "BufferDecoderGroup");
+ static final ClassName EmptyBufferDecoderGroup = ClassName.get(encodingApiPkg, "EmptyBufferDecoderGroup");
+ static final ClassName BufferEncoder = ClassName.get(encodingApiPkg, "BufferEncoder");
static final TypeName BufferEncoderList = ParameterizedTypeName.get(List, BufferEncoder);
- static final ClassName ContentCodec = bestGuess(encodingApiPkg + ".ContentCodec");
+ static final ClassName ContentCodec = ClassName.get(encodingApiPkg, "ContentCodec");
static final TypeName GrpcSupportedCodings = ParameterizedTypeName.get(List, ContentCodec);
- static final ClassName GrpcPayloadWriter = bestGuess(grpcApiPkg + ".GrpcPayloadWriter");
- static final ClassName GrpcRoutes = bestGuess(grpcApiPkg + ".GrpcRoutes");
- static final ClassName GrpcSerializationProvider = bestGuess(grpcApiPkg + ".GrpcSerializationProvider");
- static final ClassName GrpcBindableService = bestGuess(grpcApiPkg + ".GrpcBindableService");
- static final ClassName GrpcService = bestGuess(grpcApiPkg + ".GrpcService");
- static final ClassName GrpcServiceContext = bestGuess(grpcApiPkg + ".GrpcServiceContext");
- static final ClassName GrpcServiceFactory = bestGuess(grpcApiPkg + ".GrpcServiceFactory");
- static final ClassName GrpcMethodDescriptor = bestGuess(grpcApiPkg + ".MethodDescriptor");
- static final ClassName GrpcMethodDescriptors = bestGuess(grpcApiPkg + ".MethodDescriptors");
+ static final ClassName GrpcPayloadWriter = ClassName.get(grpcApiPkg, "GrpcPayloadWriter");
+ static final ClassName GrpcRoutes = ClassName.get(grpcApiPkg, "GrpcRoutes");
+ static final ClassName GrpcSerializationProvider = ClassName.get(grpcApiPkg, "GrpcSerializationProvider");
+ static final ClassName GrpcBindableService = ClassName.get(grpcApiPkg, "GrpcBindableService");
+ static final ClassName GrpcService = ClassName.get(grpcApiPkg, "GrpcService");
+ static final ClassName GrpcServiceContext = ClassName.get(grpcApiPkg, "GrpcServiceContext");
+ static final ClassName GrpcServiceFactory = ClassName.get(grpcApiPkg, "GrpcServiceFactory");
+ static final ClassName GrpcMethodDescriptor = ClassName.get(grpcApiPkg, "MethodDescriptor");
+ static final ClassName GrpcMethodDescriptors = ClassName.get(grpcApiPkg, "MethodDescriptors");
static final ParameterizedTypeName GrpcMethodDescriptorCollection = ParameterizedTypeName.get(Collection,
ParameterizedTypeName.get(GrpcMethodDescriptor, Wildcard, Wildcard));
- static final ClassName BlockingClientCall = bestGuess(GrpcClientCallFactory + ".BlockingClientCall");
+ static final ClassName BlockingClientCall = GrpcClientCallFactory.nestedClass("BlockingClientCall");
static final ClassName BlockingRequestStreamingClientCall =
- bestGuess(GrpcClientCallFactory + ".BlockingRequestStreamingClientCall");
+ GrpcClientCallFactory.nestedClass("BlockingRequestStreamingClientCall");
static final ClassName BlockingResponseStreamingClientCall =
- bestGuess(GrpcClientCallFactory + ".BlockingResponseStreamingClientCall");
+ GrpcClientCallFactory.nestedClass("BlockingResponseStreamingClientCall");
static final ClassName BlockingStreamingClientCall =
- bestGuess(GrpcClientCallFactory + ".BlockingStreamingClientCall");
- static final ClassName ClientCall = bestGuess(GrpcClientCallFactory + ".ClientCall");
+ GrpcClientCallFactory.nestedClass("BlockingStreamingClientCall");
+ static final ClassName ClientCall = GrpcClientCallFactory.nestedClass("ClientCall");
static final ClassName RequestStreamingClientCall =
- bestGuess(GrpcClientCallFactory + ".RequestStreamingClientCall");
+ GrpcClientCallFactory.nestedClass("RequestStreamingClientCall");
static final ClassName ResponseStreamingClientCall =
- bestGuess(GrpcClientCallFactory + ".ResponseStreamingClientCall");
- static final ClassName StreamingClientCall = bestGuess(GrpcClientCallFactory + ".StreamingClientCall");
+ GrpcClientCallFactory.nestedClass("ResponseStreamingClientCall");
+ static final ClassName StreamingClientCall = GrpcClientCallFactory.nestedClass("StreamingClientCall");
+ // Inner protected types need bestGuess to avoid adding imports which aren't visible and causing compile problems.
static final ClassName AllGrpcRoutes = bestGuess(grpcRoutesFqcn + ".AllGrpcRoutes");
static final ClassName RequestStreamingRoute = bestGuess(grpcRoutesFqcn + ".RequestStreamingRoute");
static final ClassName ResponseStreamingRoute = bestGuess(grpcRoutesFqcn + ".ResponseStreamingRoute");
static final ClassName Route = bestGuess(grpcRoutesFqcn + ".Route");
static final ClassName StreamingRoute = bestGuess(grpcRoutesFqcn + ".StreamingRoute");
- static final ClassName BlockingRequestStreamingRoute = bestGuess(grpcRoutesFqcn + ".BlockingRequestStreamingRoute");
- static final ClassName BlockingResponseStreamingRoute = bestGuess(grpcRoutesFqcn +
- ".BlockingResponseStreamingRoute");
+ static final ClassName BlockingRequestStreamingRoute =
+ bestGuess(grpcRoutesFqcn + ".BlockingRequestStreamingRoute");
+ static final ClassName BlockingResponseStreamingRoute =
+ bestGuess(grpcRoutesFqcn + ".BlockingResponseStreamingRoute");
static final ClassName BlockingRoute = bestGuess(grpcRoutesFqcn + ".BlockingRoute");
static final ClassName BlockingStreamingRoute = bestGuess(grpcRoutesFqcn + ".BlockingStreamingRoute");
@Deprecated
static final ClassName ProtoBufSerializationProviderBuilder =
- bestGuess(grpcProtobufPkg + ".ProtoBufSerializationProviderBuilder");
- static final ClassName ProtobufSerializerFactory = bestGuess(protobufDataPkg + ".ProtobufSerializerFactory");
+ ClassName.get(grpcProtobufPkg, "ProtoBufSerializationProviderBuilder");
+ static final ClassName ProtobufSerializerFactory = ClassName.get(protobufDataPkg, "ProtobufSerializerFactory");
- static final TypeName GrpcRouteExecutionStrategyFactory = ParameterizedTypeName.get(RouteExecutionStrategyFactory,
- GrpcExecutionStrategy);
+ static final TypeName GrpcRouteExecutionStrategyFactory =
+ ParameterizedTypeName.get(RouteExecutionStrategyFactory, GrpcExecutionStrategy);
private Types() {
// no instances
diff --git a/servicetalk-grpc-protoc/src/test/java/io/servicetalk/grpc/protoc/TestConflictEnum.java b/servicetalk-grpc-protoc/src/test/java/io/servicetalk/grpc/protoc/TestConflictEnum.java
new file mode 100644
index 0000000000..862265d96b
--- /dev/null
+++ b/servicetalk-grpc-protoc/src/test/java/io/servicetalk/grpc/protoc/TestConflictEnum.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.servicetalk.grpc.protoc;
+
+import io.servicetalk.concurrent.api.Single;
+import io.servicetalk.grpc.api.GrpcServiceContext;
+import io.servicetalk.grpc.protoc.test.conflict.enums.TestConflictEnum2OuterClass.TestConflictEnum2Resp;
+import io.servicetalk.grpc.protoc.test.conflict.enums.TestConflictEnum2OuterClass.TestConflictEnum2Service.TestConflictEnum2ServiceService;
+import io.servicetalk.grpc.protoc.test.conflict.enums.TestConflictEnum3Resp;
+import io.servicetalk.grpc.protoc.test.conflict.enums.TestConflictEnum3Service.TestConflictEnum3ServiceService;
+import io.servicetalk.grpc.protoc.test.conflict.enums.TestConflictEnumInsideOuterClass.TestConflictEnumInside.TestConflictEnumInsideService;
+import io.servicetalk.grpc.protoc.test.conflict.enums.TestConflictEnumInsideOuterClass.TestConflictEnumInsideResp;
+import io.servicetalk.grpc.protoc.test.conflict.enums.TestConflictEnumReq;
+import io.servicetalk.grpc.protoc.test.conflict.enums.TestConflictEnumResp;
+import io.servicetalk.grpc.protoc.test.conflict.enums.TestConflictEnumService.TestConflictEnumServiceService;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.ExecutionException;
+
+import static io.servicetalk.concurrent.api.Single.succeeded;
+
+class TestConflictEnum {
+ @Test
+ void enumConflict() throws ExecutionException, InterruptedException {
+ new TestConflictEnumServiceService() {
+ @Override
+ public Single doNothingRpc(
+ final GrpcServiceContext ctx, final TestConflictEnumReq request) {
+ return succeeded(TestConflictEnumResp.newBuilder().build());
+ }
+
+ @Override
+ public Single doNothing(
+ final GrpcServiceContext ctx, final TestConflictEnumReq request) {
+ return succeeded(TestConflictEnumResp.newBuilder().build());
+ }
+ }.closeAsync().toFuture().get();
+ }
+
+ @Test
+ void enum2Conflict() throws ExecutionException, InterruptedException {
+ ((TestConflictEnum2ServiceService) (ctx, request) -> succeeded(TestConflictEnum2Resp.newBuilder().build()))
+ .closeAsync().toFuture().get();
+ }
+
+ @Test
+ void enum3Conflict() throws ExecutionException, InterruptedException {
+ ((TestConflictEnum3ServiceService) (ctx, request) -> succeeded(TestConflictEnum3Resp.newBuilder().build()))
+ .closeAsync().toFuture().get();
+ }
+
+ @Test
+ void enumInsideConflict() throws ExecutionException, InterruptedException {
+ ((TestConflictEnumInsideService) (ctx, request) -> succeeded(TestConflictEnumInsideResp.newBuilder().build()))
+ .closeAsync().toFuture().get();
+ }
+}
diff --git a/servicetalk-grpc-protoc/src/test/java/io/servicetalk/grpc/protoc/TestConflictMessage.java b/servicetalk-grpc-protoc/src/test/java/io/servicetalk/grpc/protoc/TestConflictMessage.java
new file mode 100644
index 0000000000..8e97a8a589
--- /dev/null
+++ b/servicetalk-grpc-protoc/src/test/java/io/servicetalk/grpc/protoc/TestConflictMessage.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.servicetalk.grpc.protoc;
+
+import io.servicetalk.grpc.protoc.test.conflict.message.TestConflictMessageOuterClass.TestConflictMessageService.TestConflictMessageServiceService;
+import io.servicetalk.grpc.protoc.test.conflict.message.TestConflictMessageOuterClass.TestConflictResp;
+import io.servicetalk.grpc.protoc.test.conflict.message.TestConflictMessageService2.TestConflictMessageService2Service;
+import io.servicetalk.grpc.protoc.test.conflict.message.TestConflictMessageService3.TestConflictMessageService3Service;
+import io.servicetalk.grpc.protoc.test.conflict.message.TestConflictResp2;
+import io.servicetalk.grpc.protoc.test.conflict.message.TestConflictResp3;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.ExecutionException;
+
+import static io.servicetalk.concurrent.api.Single.succeeded;
+
+class TestConflictMessage {
+ @Test
+ void messageConflict() throws ExecutionException, InterruptedException {
+ ((TestConflictMessageServiceService) (ctx, request) -> succeeded(TestConflictResp.newBuilder().build()))
+ .closeAsync().toFuture().get();
+ }
+
+ @Test
+ void message2Conflict() throws ExecutionException, InterruptedException {
+ ((TestConflictMessageService2Service) (ctx, request) -> succeeded(TestConflictResp2.newBuilder().build()))
+ .closeAsync().toFuture().get();
+ }
+
+ @Test
+ void message3Conflict() throws ExecutionException, InterruptedException {
+ ((TestConflictMessageService3Service) (ctx, request) -> succeeded(TestConflictResp3.newBuilder().build()))
+ .closeAsync().toFuture().get();
+ }
+}
diff --git a/servicetalk-grpc-protoc/src/test/java/io/servicetalk/grpc/protoc/TestConflictMultiService.java b/servicetalk-grpc-protoc/src/test/java/io/servicetalk/grpc/protoc/TestConflictMultiService.java
index 98f99876a3..3d1ca9b444 100644
--- a/servicetalk-grpc-protoc/src/test/java/io/servicetalk/grpc/protoc/TestConflictMultiService.java
+++ b/servicetalk-grpc-protoc/src/test/java/io/servicetalk/grpc/protoc/TestConflictMultiService.java
@@ -16,7 +16,7 @@
package io.servicetalk.grpc.protoc;
import io.servicetalk.concurrent.api.Single;
-import io.servicetalk.grpc.protoc.test.conflict.multi.service.TestConflictMultiService0.TestConflictMultiServiceService;
+import io.servicetalk.grpc.protoc.test.conflict.multi.service.TestConflictMultiService.TestConflictMultiServiceService;
import io.servicetalk.grpc.protoc.test.conflict.multi.service.TestReply;
import org.junit.jupiter.api.Test;
diff --git a/servicetalk-grpc-protoc/src/test/java/io/servicetalk/grpc/protoc/TestConflictService.java b/servicetalk-grpc-protoc/src/test/java/io/servicetalk/grpc/protoc/TestConflictService.java
index d637403b37..2fa63fee00 100644
--- a/servicetalk-grpc-protoc/src/test/java/io/servicetalk/grpc/protoc/TestConflictService.java
+++ b/servicetalk-grpc-protoc/src/test/java/io/servicetalk/grpc/protoc/TestConflictService.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2020 Apple Inc. and the ServiceTalk project authors
+ * Copyright © 2020, 2022 Apple Inc. and the ServiceTalk project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,19 +16,74 @@
package io.servicetalk.grpc.protoc;
import io.servicetalk.concurrent.api.Single;
+import io.servicetalk.grpc.api.GrpcServiceContext;
+import io.servicetalk.grpc.protoc.test.conflict.message.TestConflictMessageService2.TestConflictMessageService2Service;
+import io.servicetalk.grpc.protoc.test.conflict.message.TestConflictMessageService3.TestConflictMessageService3Service;
+import io.servicetalk.grpc.protoc.test.conflict.message.TestConflictResp2;
+import io.servicetalk.grpc.protoc.test.conflict.message.TestConflictResp3;
import io.servicetalk.grpc.protoc.test.conflict.service.TestConflictService.TestConflict.TestConflictService0;
+import io.servicetalk.grpc.protoc.test.conflict.service.TestConflictService4OuterClass.TestConflictService4.TestConflictService4Service;
+import io.servicetalk.grpc.protoc.test.conflict.service.TestConflictService4OuterClass.TestConflictService4Resp;
+import io.servicetalk.grpc.protoc.test.conflict.service.TestConflictService5.TestConflictService50.TestConflictService5Service;
+import io.servicetalk.grpc.protoc.test.conflict.service.TestConflictService5.testConflictService5Req;
+import io.servicetalk.grpc.protoc.test.conflict.service.TestConflictService5.testConflictService5Resp;
+import io.servicetalk.grpc.protoc.test.conflict.service.TestConflictService5.test_conflict_service_5_req;
+import io.servicetalk.grpc.protoc.test.conflict.service.TestConflictService5.test_conflict_service_5_resp;
+import io.servicetalk.grpc.protoc.test.conflict.service.TestConflictServiceOuterClassOuterClass.TestConflictServiceOuterClass.TestConflictServiceOuterClassService;
+import io.servicetalk.grpc.protoc.test.conflict.service.TestConflictServiceOuterClassOuterClass.TestConflictServiceOuterServiceResp;
import org.junit.jupiter.api.Test;
import java.util.concurrent.ExecutionException;
+import static io.servicetalk.concurrent.api.Single.succeeded;
import static io.servicetalk.grpc.protoc.test.conflict.service.TestConflictService.TestReply;
class TestConflictService {
@Test
- void conflictRpcServiceGenerated() throws ExecutionException, InterruptedException {
- TestConflictService0 service = (ctx, request) -> Single.succeeded(TestReply.newBuilder().build());
-
+ void conflictServiceGenerated() throws ExecutionException, InterruptedException {
+ TestConflictService0 service = (ctx, request) -> succeeded(TestReply.newBuilder().build());
service.closeAsync().toFuture().get();
}
+
+ @Test
+ void conflict2ServiceGenerated() throws ExecutionException, InterruptedException {
+ ((TestConflictMessageService2Service) (ctx, request) -> succeeded(TestConflictResp2.newBuilder().build()))
+ .closeAsync().toFuture().get();
+ }
+
+ @Test
+ void conflict3ServiceGenerated() throws ExecutionException, InterruptedException {
+ ((TestConflictMessageService3Service) (ctx, request) -> succeeded(TestConflictResp3.newBuilder().build()))
+ .closeAsync().toFuture().get();
+ }
+
+ @Test
+ void conflict4ServiceGenerated() throws ExecutionException, InterruptedException {
+ ((TestConflictService4Service) (ctx, request) -> succeeded(TestConflictService4Resp.newBuilder().build()))
+ .closeAsync().toFuture().get();
+ }
+
+ @Test
+ void conflict5ServiceGenerated() throws ExecutionException, InterruptedException {
+ new TestConflictService5Service() {
+ @Override
+ public Single doNothing2(
+ final GrpcServiceContext ctx, final test_conflict_service_5_req request) {
+ return succeeded(test_conflict_service_5_resp.newBuilder().build());
+ }
+
+ @Override
+ public Single doNothing(
+ final GrpcServiceContext ctx, final testConflictService5Req request) {
+ return succeeded(testConflictService5Resp.newBuilder().build());
+ }
+ }.closeAsync().toFuture().get();
+ }
+
+ @Test
+ void conflictOuterServiceGenerated() throws ExecutionException, InterruptedException {
+ ((TestConflictServiceOuterClassService) (ctx, request) ->
+ succeeded(TestConflictServiceOuterServiceResp.newBuilder().build())).closeAsync().toFuture().get();
+ }
}
diff --git a/servicetalk-grpc-protoc/src/test/proto/test_conflict_enum.proto b/servicetalk-grpc-protoc/src/test/proto/test_conflict_enum.proto
new file mode 100644
index 0000000000..261db8bd14
--- /dev/null
+++ b/servicetalk-grpc-protoc/src/test/proto/test_conflict_enum.proto
@@ -0,0 +1,41 @@
+//
+// Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Test file name conflicts with a top level enum -> generate code into the right location
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "io.servicetalk.grpc.protoc.test.conflict.enums";
+
+// No java_outer_classname provided to make sure the inferred outer classname doesn't clash
+// with the generated TestConflictMultiService inner interface
+
+service TestConflictEnumService {
+ rpc DoNothing (TestConflictEnumReq) returns (TestConflictEnumResp);
+ // This name will conflict with internal generated interface.
+ rpc DoNothingRpc (TestConflictEnumReq) returns (TestConflictEnumResp);
+}
+
+enum TestConflictEnum { // This name conflicts with the file name!
+ A = 0;
+}
+
+message DoNothingRpc {} // This name will conflict with internal generated interface.
+
+message TestConflictEnumReq {
+ TestConflictEnum enum = 1;
+}
+
+message TestConflictEnumResp {}
\ No newline at end of file
diff --git a/servicetalk-grpc-protoc/src/test/proto/test_conflict_enum2.proto b/servicetalk-grpc-protoc/src/test/proto/test_conflict_enum2.proto
new file mode 100644
index 0000000000..6e5b1853ee
--- /dev/null
+++ b/servicetalk-grpc-protoc/src/test/proto/test_conflict_enum2.proto
@@ -0,0 +1,34 @@
+//
+// Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Test file name conflicts with a top level enum -> generate code into the right location
+syntax = "proto3";
+
+option java_multiple_files = false;
+option java_package = "io.servicetalk.grpc.protoc.test.conflict.enums";
+
+service TestConflictEnum2Service {
+ rpc DoNothing (TestConflictEnum2Req) returns (TestConflictEnum2Resp);
+}
+
+enum TestConflictEnum2 { // This name conflicts with the file name!
+ A2 = 0;
+}
+
+message TestConflictEnum2Req {
+ TestConflictEnum2 enum = 1;
+}
+
+message TestConflictEnum2Resp {}
\ No newline at end of file
diff --git a/servicetalk-grpc-protoc/src/test/proto/test_conflict_enum3.proto b/servicetalk-grpc-protoc/src/test/proto/test_conflict_enum3.proto
new file mode 100644
index 0000000000..e992d7948d
--- /dev/null
+++ b/servicetalk-grpc-protoc/src/test/proto/test_conflict_enum3.proto
@@ -0,0 +1,36 @@
+//
+// Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Test file name conflicts with a top level enum -> generate code into the right location
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "io.servicetalk.grpc.protoc.test.conflict.enums";
+
+service TestConflictEnum3Service {
+ rpc DoNothing (TestConflictEnum3Req) returns (TestConflictEnum3Resp);
+}
+
+// todo(scott): make first char lowercase
+// https://github.com/protocolbuffers/protobuf/issues/9653
+enum TestConflictEnum3 { // This name conflicts with the file name!
+ A3 = 0;
+}
+
+message TestConflictEnum3Req {
+ TestConflictEnum3 enum = 1;
+}
+
+message TestConflictEnum3Resp {}
\ No newline at end of file
diff --git a/servicetalk-grpc-protoc/src/test/proto/test_conflict_enum_inside.proto b/servicetalk-grpc-protoc/src/test/proto/test_conflict_enum_inside.proto
new file mode 100644
index 0000000000..dbeb871088
--- /dev/null
+++ b/servicetalk-grpc-protoc/src/test/proto/test_conflict_enum_inside.proto
@@ -0,0 +1,33 @@
+//
+// Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Test file name conflicts with a top level enum -> generate code into the right location
+syntax = "proto3";
+
+option java_multiple_files = false;
+option java_package = "io.servicetalk.grpc.protoc.test.conflict.enums";
+
+service TestConflictEnumInside {
+ rpc DoNothing (TestConflictEnumInsideReq) returns (TestConflictEnumInsideResp);
+}
+
+message TestConflictEnumInsideReq {
+ enum TestConflictEnumInside { // This name conflicts with the file name!
+ A2 = 0;
+ }
+ TestConflictEnumInside enum = 1;
+}
+
+message TestConflictEnumInsideResp {}
\ No newline at end of file
diff --git a/servicetalk-grpc-protoc/src/test/proto/test_conflict_message.proto b/servicetalk-grpc-protoc/src/test/proto/test_conflict_message.proto
new file mode 100644
index 0000000000..554b6a97d2
--- /dev/null
+++ b/servicetalk-grpc-protoc/src/test/proto/test_conflict_message.proto
@@ -0,0 +1,30 @@
+//
+// Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Test file name conflicts with a top level message -> generate code into the right location
+syntax = "proto3";
+
+option java_multiple_files = false;
+option java_package = "io.servicetalk.grpc.protoc.test.conflict.message";
+
+// No java_outer_classname provided to make sure the inferred outer classname doesn't clash
+// with the generated TestConflictMultiService inner interface
+
+service TestConflictMessageService {
+ rpc DoNothing (TestConflictMessage) returns (TestConflictResp);
+}
+
+message TestConflictMessage {} // This name conflicts with the file name!
+message TestConflictResp {}
\ No newline at end of file
diff --git a/servicetalk-grpc-protoc/src/test/proto/test_conflict_message2.proto b/servicetalk-grpc-protoc/src/test/proto/test_conflict_message2.proto
new file mode 100644
index 0000000000..9246fd2206
--- /dev/null
+++ b/servicetalk-grpc-protoc/src/test/proto/test_conflict_message2.proto
@@ -0,0 +1,29 @@
+//
+// Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "io.servicetalk.grpc.protoc.test.conflict.message";
+
+// No java_outer_classname provided to make sure the inferred outer classname doesn't clash
+// with the generated type names.
+
+service TestConflictMessageService2 {
+ rpc DoNothing (TestConflictMessage2) returns (TestConflictResp2);
+}
+
+message TestConflictMessage2 {} // This name conflicts with the file name!
+message TestConflictResp2 {}
\ No newline at end of file
diff --git a/servicetalk-grpc-protoc/src/test/proto/test_conflict_message3.proto b/servicetalk-grpc-protoc/src/test/proto/test_conflict_message3.proto
new file mode 100644
index 0000000000..afe9c08490
--- /dev/null
+++ b/servicetalk-grpc-protoc/src/test/proto/test_conflict_message3.proto
@@ -0,0 +1,31 @@
+//
+// Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "io.servicetalk.grpc.protoc.test.conflict.message";
+
+// No java_outer_classname provided to make sure the inferred outer classname doesn't clash
+// with the generated type names.
+
+service TestConflictMessageService3 {
+ rpc DoNothing (TestConflictMessage3) returns (TestConflictResp3);
+}
+
+// todo(scott): make first char lowercase
+// https://github.com/protocolbuffers/protobuf/issues/9653
+message TestConflictMessage3 {} // This name conflicts with the file name!
+message TestConflictResp3 {}
\ No newline at end of file
diff --git a/servicetalk-grpc-protoc/src/test/proto/test_conflict_service2.proto b/servicetalk-grpc-protoc/src/test/proto/test_conflict_service2.proto
new file mode 100644
index 0000000000..2e59db1688
--- /dev/null
+++ b/servicetalk-grpc-protoc/src/test/proto/test_conflict_service2.proto
@@ -0,0 +1,26 @@
+//
+// Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+option java_multiple_files = false;
+option java_package = "io.servicetalk.grpc.protoc.test.conflict.service";
+
+service TestConflictService2 {
+ rpc DoNothing (TestConflictService2Req) returns (TestConflictService2Resp);
+}
+
+message TestConflictService2Req {}
+message TestConflictService2Resp {}
\ No newline at end of file
diff --git a/servicetalk-grpc-protoc/src/test/proto/test_conflict_service3.proto b/servicetalk-grpc-protoc/src/test/proto/test_conflict_service3.proto
new file mode 100644
index 0000000000..c194badb3c
--- /dev/null
+++ b/servicetalk-grpc-protoc/src/test/proto/test_conflict_service3.proto
@@ -0,0 +1,26 @@
+//
+// Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "io.servicetalk.grpc.protoc.test.conflict.service";
+
+service TestConflictService3 {
+ rpc DoNothing (TestConflictService3Req) returns (TestConflictService3Resp);
+}
+
+message TestConflictService3Req {}
+message TestConflictService3Resp {}
\ No newline at end of file
diff --git a/servicetalk-grpc-protoc/src/test/proto/test_conflict_service4.proto b/servicetalk-grpc-protoc/src/test/proto/test_conflict_service4.proto
new file mode 100644
index 0000000000..f9a3d4ec67
--- /dev/null
+++ b/servicetalk-grpc-protoc/src/test/proto/test_conflict_service4.proto
@@ -0,0 +1,26 @@
+//
+// Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+option java_multiple_files = false;
+option java_package = "io.servicetalk.grpc.protoc.test.conflict.service";
+
+service TestConflictService4 {
+ rpc DoNothing (TestConflictService4Service) returns (TestConflictService4Resp);
+}
+
+message TestConflictService4Service {} // Conflicts with the name of the generated type.
+message TestConflictService4Resp {}
\ No newline at end of file
diff --git a/servicetalk-grpc-protoc/src/test/proto/test_conflict_service5.proto b/servicetalk-grpc-protoc/src/test/proto/test_conflict_service5.proto
new file mode 100644
index 0000000000..430d3fbcc4
--- /dev/null
+++ b/servicetalk-grpc-protoc/src/test/proto/test_conflict_service5.proto
@@ -0,0 +1,29 @@
+//
+// Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+option java_multiple_files = false;
+option java_package = "io.servicetalk.grpc.protoc.test.conflict.service";
+
+service testConflictService5 {
+ rpc DoNothing (testConflictService5Req) returns (testConflictService5Resp);
+ rpc DoNothing2 (test_conflict_service_5_req) returns (test_conflict_service_5_resp);
+}
+
+message testConflictService5Req {}
+message test_conflict_service_5_req {}
+message testConflictService5Resp {}
+message test_conflict_service_5_resp {}
\ No newline at end of file
diff --git a/servicetalk-grpc-protoc/src/test/proto/test_conflict_service_outer_class.proto b/servicetalk-grpc-protoc/src/test/proto/test_conflict_service_outer_class.proto
new file mode 100644
index 0000000000..09419882bd
--- /dev/null
+++ b/servicetalk-grpc-protoc/src/test/proto/test_conflict_service_outer_class.proto
@@ -0,0 +1,26 @@
+//
+// Copyright © 2022 Apple Inc. and the ServiceTalk project authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+option java_multiple_files = false;
+option java_package = "io.servicetalk.grpc.protoc.test.conflict.service";
+
+service TestConflictServiceOuterClass {
+ rpc DoNothing (TestConflictServiceOuterServiceReq) returns (TestConflictServiceOuterServiceResp);
+}
+
+message TestConflictServiceOuterServiceReq {}
+message TestConflictServiceOuterServiceResp {}
\ No newline at end of file