From 0df652ced14799f65c2245fe6a8c44ae1d8bf618 Mon Sep 17 00:00:00 2001 From: Nadav Samet Date: Wed, 22 Nov 2023 15:48:28 -0800 Subject: [PATCH] Enum values that repeat the same number become aliases. Fixes #1611 --- CHANGELOG.md | 5 ++++ .../scalapb/compiler/ProtobufGenerator.scala | 27 +++++++++++++------ .../src/main/protobuf/enum_alias.proto | 11 ++++++++ e2e/src/test/scala/EnumAliasSpec.scala | 17 ++++++++++++ 4 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 e2e-withjava/src/main/protobuf/enum_alias.proto create mode 100644 e2e/src/test/scala/EnumAliasSpec.scala diff --git a/CHANGELOG.md b/CHANGELOG.md index a8227db03..d3cb2dbf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## [0.11.15] +- When more than one enum values share the same number, the first one would + generate a case object as usual, and subsequent ones will value values + referencing the case object. This is consistent with the Java behavior (#1611) + ## [0.11.14] - Repeated extensions can now use arbitrary iterable types or any collection type there is a CollectionAdapter for (#1509) diff --git a/compiler-plugin/src/main/scala/scalapb/compiler/ProtobufGenerator.scala b/compiler-plugin/src/main/scala/scalapb/compiler/ProtobufGenerator.scala index 123f04852..8a47336a2 100644 --- a/compiler-plugin/src/main/scala/scalapb/compiler/ProtobufGenerator.scala +++ b/compiler-plugin/src/main/scala/scalapb/compiler/ProtobufGenerator.scala @@ -24,7 +24,8 @@ class ProtobufGenerator( import ProtobufGenerator._ def printEnum(printer: FunctionalPrinter, e: EnumDescriptor): FunctionalPrinter = { - val name = e.scalaType.nameSymbol + val name = e.scalaType.nameSymbol + val valuesByNumber = e.getValues().asScala.groupBy(_.getNumber()) printer .when(e.getOptions.getDeprecated) { _.add(ProtobufGenerator.deprecatedAnnotation) @@ -56,17 +57,27 @@ class ProtobufGenerator( .add(s"sealed trait ${e.recognizedEnum.nameSymbol} extends $name") .add(s"implicit def enumCompanion: _root_.scalapb.GeneratedEnumCompanion[$name] = this") .newline - .print(e.getValues.asScala) { case (p, v) => + .print(valuesByNumber) { case (p, (_, values)) => + val v = values.head p.call(generateScalaDoc(v)) .add("@SerialVersionUID(0L)") .seq(v.annotationList) .add(s"""case object ${v.scalaName.asSymbol} extends ${v.valueExtends - .mkString(" with ")} { - | val index = ${v.getIndex} - | val name = "${v.getName}" - | override def ${v.isName}: _root_.scala.Boolean = true - |} - |""".stripMargin) + .mkString(" with ")} {""") + .indented( + _.add(s"val index = ${v.getIndex}") + .add(s"""val name = "${v.getName}"""") + .print(values) { case (p, u) => + p.add(s"override def ${u.isName}: _root_.scala.Boolean = true") + } + ) + .add("}") + .print(values.tail) { case (p, u) => + p.call(generateScalaDoc(u)) + .seq(u.annotationList) + .add(s"@transient val ${u.scalaName.asSymbol} = ${v.scalaName.asSymbol}") + } + .add("") } .add("@SerialVersionUID(0L)") .seq(e.unrecognizedAnnotationList) diff --git a/e2e-withjava/src/main/protobuf/enum_alias.proto b/e2e-withjava/src/main/protobuf/enum_alias.proto new file mode 100644 index 000000000..01c17c439 --- /dev/null +++ b/e2e-withjava/src/main/protobuf/enum_alias.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package com.thesamet.proto.e2e; + +enum EnumAllowingAlias { + option allow_alias = true; + EAA_UNSPECIFIED = 0; + EAA_STARTED = 1; + EAA_RUNNING = 1; // some doc + EAA_FINISHED = 2; +} \ No newline at end of file diff --git a/e2e/src/test/scala/EnumAliasSpec.scala b/e2e/src/test/scala/EnumAliasSpec.scala new file mode 100644 index 000000000..66b20ff16 --- /dev/null +++ b/e2e/src/test/scala/EnumAliasSpec.scala @@ -0,0 +1,17 @@ +import com.thesamet.proto.e2e.enum_alias._ +import org.scalatest._ +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.must.Matchers + +class EnumAliasSpec extends AnyFlatSpec with Matchers with OptionValues { + "aliased enums" should "be equal" in { + EnumAllowingAlias.EAA_RUNNING must be(EnumAllowingAlias.EAA_STARTED) + } + + it should "report isX true for all aliases" in { + EnumAllowingAlias.EAA_RUNNING.isEaaRunning must be(true) + EnumAllowingAlias.EAA_RUNNING.isEaaStarted must be(true) + EnumAllowingAlias.EAA_FINISHED.isEaaStarted must be(false) + EnumAllowingAlias.EAA_FINISHED.isEaaRunning must be(false) + } +} \ No newline at end of file