From b0fa7180d8504f49b7cfa4531f6520d4e220e95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Quenaudon?= Date: Fri, 8 May 2026 17:18:53 +0100 Subject: [PATCH] PascalCase for sealed classes --- .../squareup/wire/sealedoneof/SealedOneOfs.kt | 10 ++++---- .../squareup/wire/kotlin/KotlinGenerator.kt | 15 ++++++++---- .../wire/kotlin/KotlinGeneratorTest.kt | 24 +++++++++++++++++++ .../java/com/squareup/wire/InteropTest.kt | 16 ++++++------- 4 files changed, 48 insertions(+), 17 deletions(-) diff --git a/wire-golden-files/src/main/kotlin/squareup/wire/sealedoneof/SealedOneOfs.kt b/wire-golden-files/src/main/kotlin/squareup/wire/sealedoneof/SealedOneOfs.kt index 5f9fd19c5f..1cc7199656 100644 --- a/wire-golden-files/src/main/kotlin/squareup/wire/sealedoneof/SealedOneOfs.kt +++ b/wire-golden-files/src/main/kotlin/squareup/wire/sealedoneof/SealedOneOfs.kt @@ -129,11 +129,11 @@ public class SealedOneOfs private constructor( public companion object { @JvmField public val ADAPTER: ProtoAdapter = object : ProtoAdapter( - FieldEncoding.LENGTH_DELIMITED, - SealedOneOfs::class, - "type.googleapis.com/squareup.wire.sealedoneof.SealedOneOfs", - PROTO_2, - null, + FieldEncoding.LENGTH_DELIMITED, + SealedOneOfs::class, + "type.googleapis.com/squareup.wire.sealedoneof.SealedOneOfs", + PROTO_2, + null, "squareup/wire/sealed_oneof.proto" ) { override fun encodedSize(`value`: SealedOneOfs): Int { diff --git a/wire-kotlin-generator/src/main/java/com/squareup/wire/kotlin/KotlinGenerator.kt b/wire-kotlin-generator/src/main/java/com/squareup/wire/kotlin/KotlinGenerator.kt index 845351c18c..fc786c3d2b 100644 --- a/wire-kotlin-generator/src/main/java/com/squareup/wire/kotlin/KotlinGenerator.kt +++ b/wire-kotlin-generator/src/main/java/com/squareup/wire/kotlin/KotlinGenerator.kt @@ -595,7 +595,7 @@ class KotlinGenerator private constructor( } is OneOf -> { val fieldName = newName(fieldOrOneOf.name, fieldOrOneOf) - newName(boxedOneOfClassName(fieldOrOneOf.name), boxedOneOfClassName(fieldOrOneOf.name)) + newName(oneOfClassName(fieldOrOneOf), boxedOneOfClassName(fieldOrOneOf.name)) if (oneofMode != OneofMode.SEALED_CLASS) { val keysFieldName = boxedOneOfKeysFieldName(fieldName) check(newName(keysFieldName) == keysFieldName) { @@ -3051,10 +3051,17 @@ class KotlinGenerator private constructor( } /** - * Converts a snake_case field name to PascalCase for use as a sealed class subtype name. + * Converts a snake_case name to CamelCase for use as a sealed oneof type name. * For example: `card_id` → `CardId`, `bank_account` → `BankAccount`. */ - private fun String.toSealedSubclassName(): String = split("_").joinToString("") { part -> part.replaceFirstChar { it.uppercaseChar() } } + private fun String.toCamelCase(): String = split("_").joinToString("") { part -> + part.replaceFirstChar { it.uppercaseChar() } + } + + private fun oneOfClassName(oneOf: OneOf): String = when (oneofMode) { + OneofMode.SEALED_CLASS -> oneOf.name.toCamelCase() + else -> boxedOneOfClassName(oneOf.name) + } /** * Generates a sealed class for a oneof. @@ -3324,7 +3331,7 @@ class KotlinGenerator private constructor( private fun sealedSubclassNameAllocator(oneOf: OneOf): NameAllocator = sealedSubclassNameAllocatorStore.getOrPut(oneOf) { NameAllocator(preallocateKeywords = !escapeKotlinKeywords).apply { for (field in oneOf.fields) { - newName(field.name.toSealedSubclassName(), field) + newName(field.name.toCamelCase(), field) } } } diff --git a/wire-kotlin-generator/src/test/java/com/squareup/wire/kotlin/KotlinGeneratorTest.kt b/wire-kotlin-generator/src/test/java/com/squareup/wire/kotlin/KotlinGeneratorTest.kt index 6b5c8104c9..906b1c994d 100644 --- a/wire-kotlin-generator/src/test/java/com/squareup/wire/kotlin/KotlinGeneratorTest.kt +++ b/wire-kotlin-generator/src/test/java/com/squareup/wire/kotlin/KotlinGeneratorTest.kt @@ -2246,6 +2246,30 @@ class KotlinGeneratorTest { ) } + @Test fun sealedOneofGeneratesCamelCaseClassName() { + val schema = buildSchema { + add( + "message.proto".toPath(), + """ + |syntax = "proto2"; + |message PaymentMethodChoice { + | oneof payment_method { + | string card_id = 1; + | } + |} + """.trimMargin(), + ) + } + val code = KotlinWithProfilesGenerator(schema) + .generateKotlin("PaymentMethodChoice", oneofMode = OneofMode.SEALED_CLASS) + assertThat(code).contains("public val payment_method: PaymentMethod? = null") + assertThat(code).contains("public sealed class PaymentMethod") + assertThat(code).contains("public data class CardId(") + assertThat(code).contains("is PaymentMethod.CardId ->") + assertThat(code).contains("1 -> payment_method = PaymentMethod.CardId(") + assertThat(code).doesNotContain("public sealed class Payment_method") + } + @Test fun sealedOneofAdapterEncodesAndDecodesCorrectly() { val schema = buildSchema { add( diff --git a/wire-protoc-compatibility-tests/src/test/java/com/squareup/wire/InteropTest.kt b/wire-protoc-compatibility-tests/src/test/java/com/squareup/wire/InteropTest.kt index 689af5b06d..559ee35a60 100644 --- a/wire-protoc-compatibility-tests/src/test/java/com/squareup/wire/InteropTest.kt +++ b/wire-protoc-compatibility-tests/src/test/java/com/squareup/wire/InteropTest.kt @@ -296,16 +296,16 @@ class InteropTest { ) checker.check( InteropSealedOneOfBuildersOnlyK2.Builder() - .first_method(InteropSealedOneOfBuildersOnlyK2.First_method.A("Hello")) + .first_method(InteropSealedOneOfBuildersOnlyK2.FirstMethod.A("Hello")) .h("in the middle") - .second_method(InteropSealedOneOfBuildersOnlyK2.Second_method.G(InteropSealedOneOfBuildersOnlyK2.SealedMessage.build { content("content") })) + .second_method(InteropSealedOneOfBuildersOnlyK2.SecondMethod.G(InteropSealedOneOfBuildersOnlyK2.SealedMessage.build { content("content") })) .build(), ) checker.check( InteropSealedOneOfBuildersOnlyK3.Builder() - .first_method(InteropSealedOneOfBuildersOnlyK3.First_method.A("Hello")) + .first_method(InteropSealedOneOfBuildersOnlyK3.FirstMethod.A("Hello")) .h("in the middle") - .second_method(InteropSealedOneOfBuildersOnlyK3.Second_method.G(InteropSealedOneOfBuildersOnlyK3.SealedMessage.build { content("content") })) + .second_method(InteropSealedOneOfBuildersOnlyK3.SecondMethod.G(InteropSealedOneOfBuildersOnlyK3.SealedMessage.build { content("content") })) .build(), ) } @@ -321,16 +321,16 @@ class InteropTest { ) checker.check( InteropSealedOneOfK2( - first_method = InteropSealedOneOfK2.First_method.A("Hello"), + first_method = InteropSealedOneOfK2.FirstMethod.A("Hello"), h = "in the middle", - second_method = InteropSealedOneOfK2.Second_method.G(InteropSealedOneOfK2.SealedMessage(content = "content")), + second_method = InteropSealedOneOfK2.SecondMethod.G(InteropSealedOneOfK2.SealedMessage(content = "content")), ), ) checker.check( InteropSealedOneOfK3( - first_method = InteropSealedOneOfK3.First_method.A("Hello"), + first_method = InteropSealedOneOfK3.FirstMethod.A("Hello"), h = "in the middle", - second_method = InteropSealedOneOfK3.Second_method.G(InteropSealedOneOfK3.SealedMessage(content = "content")), + second_method = InteropSealedOneOfK3.SecondMethod.G(InteropSealedOneOfK3.SealedMessage(content = "content")), ), ) }