From 1f56d5dfa0b2a2a9e5f64aea95ea55cfdbf56c64 Mon Sep 17 00:00:00 2001 From: Nadav Samet Date: Sat, 9 Nov 2019 10:59:09 -0800 Subject: [PATCH] Fix naming conflicts with oneofs Backport for 0.9.x branch Fixes #695 --- .../compiler/DescriptorImplicits.scala | 17 +++++-- .../scalapb/compiler/ProtobufGenerator.scala | 6 --- e2e/src/main/protobuf/issue695.proto | 46 +++++++++++++++++++ 3 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 e2e/src/main/protobuf/issue695.proto diff --git a/compiler-plugin/src/main/scala/scalapb/compiler/DescriptorImplicits.scala b/compiler-plugin/src/main/scala/scalapb/compiler/DescriptorImplicits.scala index 7e9fa8d39..ff1c101d2 100644 --- a/compiler-plugin/src/main/scala/scalapb/compiler/DescriptorImplicits.scala +++ b/compiler-plugin/src/main/scala/scalapb/compiler/DescriptorImplicits.scala @@ -211,9 +211,10 @@ class DescriptorImplicits(params: GeneratorParams, files: Seq[FileDescriptor]) { snakeCaseToCamelCase(fieldOptions.getScalaName, true) else fd.getName match { - case "serialized_size" => "_SerializedSize" - case "class" => "_Class" - case x => getNameWithFallback(x, Case.PascalCase, Appendage.Prefix) + case "serialized_size" => "_SerializedSize" + case "class" => "_Class" + case "empty" if fd.isInOneof => "_Empty" + case x => getNameWithFallback(x, Case.PascalCase, Appendage.Prefix) } private def getNameWithFallback(x: String, targetCase: Case, appendage: Appendage) = { @@ -443,9 +444,15 @@ class DescriptorImplicits(params: GeneratorParams, files: Seq[FileDescriptor]) { def upperScalaName = { val name = oneof.getName match { case "ValueType" | "value_type" => "ValueTypeOneof" - case n => n + case n => NameUtils.snakeCaseToCamelCase(n, true) } - NameUtils.snakeCaseToCamelCase(name, true) + + val conflictsWithMessage = + oneof.getContainingType.getEnumTypes.asScala.exists(_.name == name) || + oneof.getContainingType.nestedTypes.exists(_.scalaName == name) + + if (conflictsWithMessage) name + "Oneof" + else name } def fields: IndexedSeq[FieldDescriptor] = diff --git a/compiler-plugin/src/main/scala/scalapb/compiler/ProtobufGenerator.scala b/compiler-plugin/src/main/scala/scalapb/compiler/ProtobufGenerator.scala index 329f1ed5b..20bc625a0 100644 --- a/compiler-plugin/src/main/scala/scalapb/compiler/ProtobufGenerator.scala +++ b/compiler-plugin/src/main/scala/scalapb/compiler/ProtobufGenerator.scala @@ -103,12 +103,6 @@ class ProtobufGenerator( e.getContainingType.getEnumTypes.asScala.exists(_.name == e.upperScalaName) || e.getContainingType.nestedTypes.exists(_.scalaName == e.upperScalaName) - if (possiblyConflictingName) { - throw new GeneratorException( - s"${e.getFile.getName}: The sealed trait generated for the oneof '${e.getName}' conflicts with " + - s"another message name '${e.upperScalaName}'." - ) - } printer .add(s"sealed trait ${e.upperScalaName} extends ${e.baseClasses.mkString(" with ")} {") .indent diff --git a/e2e/src/main/protobuf/issue695.proto b/e2e/src/main/protobuf/issue695.proto new file mode 100644 index 000000000..1cc3f35a1 --- /dev/null +++ b/e2e/src/main/protobuf/issue695.proto @@ -0,0 +1,46 @@ +syntax = "proto3"; + +package com.thesamet.proto.e2e; + +message OneOfFieldNamedEmpty { + + oneof content { + Foo foo = 1; + Bar empty = 2; // tests that we avoid a conflict with the `Empty` case object + Empty baz = 3; // Message called `Empty` is fine. + } + + message Foo {} + message Bar {} + message Empty {} +} + +message OneOfNameOnSameLevel { + + Options options = 1; + + message Options { + + // sealed trait will be named FooOptionsOneof to avoid conflict with + // FooOptions + oneof foo_options { + FooOptions for = 1; + BarOptions bar = 2; + } + + message FooOptions { } + + message BarOptions { + // Works fine when using same name as + // the enclosing message. + oneof bar_options { + uint64 baz1 = 1; + Empty bar2 = 2; + } + + } + + } + + message Empty {} +}