Skip to content

Commit 7da5c16

Browse files
committed
Allow edition parsing
defaulting to proto2 behavior
1 parent 6ae7fb7 commit 7da5c16

File tree

9 files changed

+173
-44
lines changed

9 files changed

+173
-44
lines changed

wire-kotlin-generator/src/main/java/com/squareup/wire/kotlin/KotlinGenerator.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,7 +1555,7 @@ class KotlinGenerator private constructor(
15551555
.addSuperclassConstructorParameter("\n%S", type.type.typeUrl!!)
15561556
.addSuperclassConstructorParameter(
15571557
"\n%M",
1558-
MemberName(Syntax::class.asClassName(), type.syntax.name),
1558+
MemberName(Syntax::class.asClassName(), type.syntax.name()),
15591559
)
15601560
.addSuperclassConstructorParameter("\nnull")
15611561
.addSuperclassConstructorParameter("\n%S\n", type.location.path)
@@ -2469,7 +2469,7 @@ class KotlinGenerator private constructor(
24692469
.addSuperclassConstructorParameter("\n⇥%T::class", parentClassName)
24702470
.addSuperclassConstructorParameter(
24712471
"\n%M",
2472-
MemberName(Syntax::class.asClassName(), message.syntax.name),
2472+
MemberName(Syntax::class.asClassName(), message.syntax.name()),
24732473
)
24742474
.addSuperclassConstructorParameter("\n%L\n", message.identity())
24752475
.addFunction(

wire-runtime/api/wire-runtime.api

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -334,18 +334,40 @@ public final class com/squareup/wire/RuntimeEnumAdapter$Companion {
334334
public abstract interface class com/squareup/wire/Service {
335335
}
336336

337-
public final class com/squareup/wire/Syntax : java/lang/Enum {
337+
public abstract class com/squareup/wire/Syntax {
338338
public static final field Companion Lcom/squareup/wire/Syntax$Companion;
339-
public static final field PROTO_2 Lcom/squareup/wire/Syntax;
340-
public static final field PROTO_3 Lcom/squareup/wire/Syntax;
341-
public static fun getEntries ()Lkotlin/enums/EnumEntries;
342-
public fun toString ()Ljava/lang/String;
343-
public static fun valueOf (Ljava/lang/String;)Lcom/squareup/wire/Syntax;
344-
public static fun values ()[Lcom/squareup/wire/Syntax;
339+
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
340+
public final fun name ()Ljava/lang/String;
345341
}
346342

347343
public final class com/squareup/wire/Syntax$Companion {
348344
public final fun get (Ljava/lang/String;)Lcom/squareup/wire/Syntax;
345+
public final fun get (Ljava/lang/String;Z)Lcom/squareup/wire/Syntax;
346+
}
347+
348+
public final class com/squareup/wire/Syntax$Edition : com/squareup/wire/Syntax {
349+
public fun <init> (Ljava/lang/String;)V
350+
public final fun component1 ()Ljava/lang/String;
351+
public final fun copy (Ljava/lang/String;)Lcom/squareup/wire/Syntax$Edition;
352+
public static synthetic fun copy$default (Lcom/squareup/wire/Syntax$Edition;Ljava/lang/String;ILjava/lang/Object;)Lcom/squareup/wire/Syntax$Edition;
353+
public fun equals (Ljava/lang/Object;)Z
354+
public final fun getValue ()Ljava/lang/String;
355+
public fun hashCode ()I
356+
public fun toString ()Ljava/lang/String;
357+
}
358+
359+
public final class com/squareup/wire/Syntax$PROTO_2 : com/squareup/wire/Syntax {
360+
public static final field INSTANCE Lcom/squareup/wire/Syntax$PROTO_2;
361+
public fun equals (Ljava/lang/Object;)Z
362+
public fun hashCode ()I
363+
public fun toString ()Ljava/lang/String;
364+
}
365+
366+
public final class com/squareup/wire/Syntax$PROTO_3 : com/squareup/wire/Syntax {
367+
public static final field INSTANCE Lcom/squareup/wire/Syntax$PROTO_3;
368+
public fun equals (Ljava/lang/Object;)Z
369+
public fun hashCode ()I
370+
public fun toString ()Ljava/lang/String;
349371
}
350372

351373
public final class com/squareup/wire/Wire {

wire-runtime/api/wire-runtime.klib.api

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -415,18 +415,6 @@ final enum class com.squareup.wire/FieldEncoding : kotlin/Enum<com.squareup.wire
415415
final val entries // com.squareup.wire/FieldEncoding.entries|#static{}entries[0]
416416
final fun <get-entries>(): kotlin.enums/EnumEntries<com.squareup.wire/FieldEncoding> // com.squareup.wire/FieldEncoding.entries.<get-entries>|<get-entries>#static(){}[0]
417417
}
418-
final enum class com.squareup.wire/Syntax : kotlin/Enum<com.squareup.wire/Syntax> { // com.squareup.wire/Syntax|null[0]
419-
enum entry PROTO_2 // com.squareup.wire/Syntax.PROTO_2|null[0]
420-
enum entry PROTO_3 // com.squareup.wire/Syntax.PROTO_3|null[0]
421-
final fun toString(): kotlin/String // com.squareup.wire/Syntax.toString|toString(){}[0]
422-
final fun valueOf(kotlin/String): com.squareup.wire/Syntax // com.squareup.wire/Syntax.valueOf|valueOf#static(kotlin.String){}[0]
423-
final fun values(): kotlin/Array<com.squareup.wire/Syntax> // com.squareup.wire/Syntax.values|values#static(){}[0]
424-
final object Companion { // com.squareup.wire/Syntax.Companion|null[0]
425-
final fun get(kotlin/String): com.squareup.wire/Syntax // com.squareup.wire/Syntax.Companion.get|get(kotlin.String){}[0]
426-
}
427-
final val entries // com.squareup.wire/Syntax.entries|#static{}entries[0]
428-
final fun <get-entries>(): kotlin.enums/EnumEntries<com.squareup.wire/Syntax> // com.squareup.wire/Syntax.entries.<get-entries>|<get-entries>#static(){}[0]
429-
}
430418
final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlin.collections/Map<#A, #B>).com.squareup.wire.internal/redactElements(com.squareup.wire/ProtoAdapter<#B>): kotlin.collections/Map<#A, #B> // com.squareup.wire.internal/redactElements|[email protected]<0:0,0:1>(com.squareup.wire.ProtoAdapter<0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
431419
final fun <#A: kotlin/Any?, #B: kotlin/Any?> com.squareup.wire.internal/copyOf(kotlin.collections/Map<#A, #B>): kotlin.collections/MutableMap<#A, #B> // com.squareup.wire.internal/copyOf|copyOf(kotlin.collections.Map<0:0,0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
432420
final fun <#A: kotlin/Any?, #B: kotlin/Any?> com.squareup.wire.internal/copyOf(kotlin/String, kotlin.collections/Map<#A, #B>?): kotlin.collections/MutableMap<#A, #B> // com.squareup.wire.internal/copyOf|copyOf(kotlin.String;kotlin.collections.Map<0:0,0:1>?){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
@@ -552,5 +540,33 @@ open annotation class com.squareup.wire/WireRpc : kotlin/Annotation { // com.squ
552540
final val sourceFile // com.squareup.wire/WireRpc.sourceFile|{}sourceFile[0]
553541
final fun <get-sourceFile>(): kotlin/String // com.squareup.wire/WireRpc.sourceFile.<get-sourceFile>|<get-sourceFile>(){}[0]
554542
}
543+
sealed class com.squareup.wire/Syntax { // com.squareup.wire/Syntax|null[0]
544+
constructor <init>(kotlin/String) // com.squareup.wire/Syntax.<init>|<init>(kotlin.String){}[0]
545+
final class Edition : com.squareup.wire/Syntax { // com.squareup.wire/Syntax.Edition|null[0]
546+
constructor <init>(kotlin/String) // com.squareup.wire/Syntax.Edition.<init>|<init>(kotlin.String){}[0]
547+
final fun component1(): kotlin/String // com.squareup.wire/Syntax.Edition.component1|component1(){}[0]
548+
final fun copy(kotlin/String =...): com.squareup.wire/Syntax.Edition // com.squareup.wire/Syntax.Edition.copy|copy(kotlin.String){}[0]
549+
final fun equals(kotlin/Any?): kotlin/Boolean // com.squareup.wire/Syntax.Edition.equals|equals(kotlin.Any?){}[0]
550+
final fun hashCode(): kotlin/Int // com.squareup.wire/Syntax.Edition.hashCode|hashCode(){}[0]
551+
final fun toString(): kotlin/String // com.squareup.wire/Syntax.Edition.toString|toString(){}[0]
552+
final val value // com.squareup.wire/Syntax.Edition.value|{}value[0]
553+
final fun <get-value>(): kotlin/String // com.squareup.wire/Syntax.Edition.value.<get-value>|<get-value>(){}[0]
554+
}
555+
final fun name(): kotlin/String // com.squareup.wire/Syntax.name|name(){}[0]
556+
final object Companion { // com.squareup.wire/Syntax.Companion|null[0]
557+
final fun get(kotlin/String): com.squareup.wire/Syntax // com.squareup.wire/Syntax.Companion.get|get(kotlin.String){}[0]
558+
final fun get(kotlin/String, kotlin/Boolean): com.squareup.wire/Syntax // com.squareup.wire/Syntax.Companion.get|get(kotlin.String;kotlin.Boolean){}[0]
559+
}
560+
final object PROTO_2 : com.squareup.wire/Syntax { // com.squareup.wire/Syntax.PROTO_2|null[0]
561+
final fun equals(kotlin/Any?): kotlin/Boolean // com.squareup.wire/Syntax.PROTO_2.equals|equals(kotlin.Any?){}[0]
562+
final fun hashCode(): kotlin/Int // com.squareup.wire/Syntax.PROTO_2.hashCode|hashCode(){}[0]
563+
final fun toString(): kotlin/String // com.squareup.wire/Syntax.PROTO_2.toString|toString(){}[0]
564+
}
565+
final object PROTO_3 : com.squareup.wire/Syntax { // com.squareup.wire/Syntax.PROTO_3|null[0]
566+
final fun equals(kotlin/Any?): kotlin/Boolean // com.squareup.wire/Syntax.PROTO_3.equals|equals(kotlin.Any?){}[0]
567+
final fun hashCode(): kotlin/Int // com.squareup.wire/Syntax.PROTO_3.hashCode|hashCode(){}[0]
568+
final fun toString(): kotlin/String // com.squareup.wire/Syntax.PROTO_3.toString|toString(){}[0]
569+
}
570+
}
555571
// Targets: [iosArm64, iosX64, macos, tvosArm64, tvosX64]
556572
final fun <#A: kotlin/Any?> (com.squareup.wire/ProtoAdapter<#A>).com.squareup.wire/decode(platform.Foundation/NSData): #A // com.squareup.wire/decode|[email protected]<0:0>(platform.Foundation.NSData){0§<kotlin.Any?>}[0]

wire-runtime/src/commonMain/kotlin/com/squareup/wire/Syntax.kt

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,51 @@
1515
*/
1616
package com.squareup.wire
1717

18-
/** Syntax version. */
19-
enum class Syntax(private val string: String) {
20-
PROTO_2("proto2"),
21-
PROTO_3("proto3"),
22-
;
18+
/** Syntax and edition version. */
19+
sealed class Syntax(internal val string: String) {
20+
data object PROTO_2 : Syntax("proto2") {
21+
override fun toString(): String = string
22+
}
23+
data object PROTO_3 : Syntax("proto3") {
24+
override fun toString(): String = string
25+
}
26+
data class Edition(val value: String) : Syntax(value) {
27+
override fun toString(): String = value
28+
}
2329

24-
override fun toString(): String = string
30+
// Created for backward capability with when Syntax used to be an enum.
31+
fun name(): String {
32+
return when (this) {
33+
// TODO(Benoit) Edition needs to return something like `Edition(<value>)`.
34+
is Edition,
35+
PROTO_2,
36+
-> "PROTO_2"
37+
PROTO_3 -> "PROTO_3"
38+
}
39+
}
2540

2641
companion object {
42+
@Deprecated("Use get(string: String, edition: Boolean) instead", ReplaceWith("Syntax.get(string, edition)"))
2743
operator fun get(string: String): Syntax {
28-
for (syntax in values()) {
29-
if (syntax.string == string) return syntax
30-
}
44+
if (string == PROTO_2.toString()) return PROTO_2
45+
if (string == PROTO_3.toString()) return PROTO_3
3146
throw IllegalArgumentException("unexpected syntax: $string")
3247
}
48+
49+
fun get(string: String, edition: Boolean): Syntax {
50+
if (edition) {
51+
if (string in KNOWN_EDITIONS) return Edition(string)
52+
throw IllegalArgumentException("unknown edition: $string")
53+
} else {
54+
if (string == PROTO_2.toString()) return PROTO_2
55+
if (string == PROTO_3.toString()) return PROTO_3
56+
throw IllegalArgumentException("unexpected syntax: $string")
57+
}
58+
}
59+
60+
private val KNOWN_EDITIONS = listOf(
61+
"2023",
62+
"2024",
63+
)
3364
}
3465
}

wire-schema/src/commonMain/kotlin/com/squareup/wire/schema/SyntaxRules.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ interface SyntaxRules {
4040
companion object {
4141
fun get(syntax: Syntax?): SyntaxRules {
4242
return when (syntax) {
43-
PROTO_3 -> PROTO_3_SYNTAX_RULES
44-
PROTO_2,
43+
is PROTO_3 -> PROTO_3_SYNTAX_RULES
44+
is Syntax.Edition,
45+
is PROTO_2,
4546
null,
4647
-> PROTO_2_SYNTAX_RULES
4748
}

wire-schema/src/commonMain/kotlin/com/squareup/wire/schema/internal/parser/ProtoFileElement.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.squareup.wire.schema.internal.parser
1717

1818
import com.squareup.wire.Syntax
19+
import com.squareup.wire.Syntax.Edition
1920
import com.squareup.wire.schema.Location
2021
import kotlin.jvm.JvmStatic
2122

@@ -37,7 +38,11 @@ data class ProtoFileElement(
3738

3839
if (syntax != null) {
3940
append('\n')
40-
append("syntax = \"$syntax\";\n")
41+
when (syntax) {
42+
is Edition -> append("edition")
43+
else -> append("syntax")
44+
}
45+
append(" = \"$syntax\";\n")
4146
}
4247
if (packageName != null) {
4348
append('\n')

wire-schema/src/commonMain/kotlin/com/squareup/wire/schema/internal/parser/ProtoParser.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,15 @@ class ProtoParser internal constructor(
121121
null
122122
}
123123

124-
label == "syntax" && context.permitsSyntax() -> {
125-
reader.expect(syntax == null, location) { "too many syntax definitions" }
124+
(label == "syntax" || label == "edition") && context.permitsSyntax() -> {
125+
reader.expect(syntax == null, location) { "too many syntax or edition definitions" }
126126
reader.require('=')
127127
reader.expect(index == 0, location) {
128-
"'syntax' element must be the first declaration in a file"
128+
"'syntax' or 'edition' element must be the first declaration in a file"
129129
}
130130
val syntaxString = reader.readQuotedString()
131131
try {
132-
syntax = Syntax[syntaxString]
132+
syntax = Syntax.get(syntaxString, edition = label == "edition")
133133
} catch (e: IllegalArgumentException) {
134134
throw reader.unexpected(e.message!!, location)
135135
}

wire-schema/src/commonTest/kotlin/com/squareup/wire/schema/internal/parser/ProtoParserTest.kt

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import assertk.assertions.hasMessage
2121
import assertk.assertions.isEqualTo
2222
import assertk.assertions.isNull
2323
import assertk.assertions.messageContains
24+
import com.squareup.wire.Syntax.Edition
2425
import com.squareup.wire.Syntax.PROTO_2
2526
import com.squareup.wire.Syntax.PROTO_3
2627
import com.squareup.wire.schema.Field.Label.OPTIONAL
@@ -627,6 +628,48 @@ class ProtoParserTest {
627628
assertThat(parsed.syntax).isNull()
628629
}
629630

631+
@Test
632+
fun editionParsing() {
633+
val proto = """
634+
|edition = "2024";
635+
|message Foo {}
636+
""".trimMargin()
637+
val parsed = ProtoParser.parse(location, proto)
638+
assertThat(parsed.syntax).isEqualTo(Edition("2024"))
639+
}
640+
641+
@Test
642+
fun syntaxAndEditionConflictAreNotAllowed() {
643+
val proto = """
644+
|edition = "2024";
645+
|syntax = "proto2";
646+
|message Foo {}
647+
""".trimMargin()
648+
try {
649+
ProtoParser.parse(location, proto)
650+
fail()
651+
} catch (e: IllegalStateException) {
652+
assertThat(e).hasMessage("Syntax error in file.proto:2:1: too many syntax or edition definitions")
653+
}
654+
}
655+
656+
@Test
657+
fun rejectUnknownEditions() {
658+
for (edition in listOf("something", "EDITION_2023", "2025")) {
659+
val proto = """
660+
|edition = "2025";
661+
|syntax = "proto2";
662+
|message Foo {}
663+
""".trimMargin()
664+
try {
665+
ProtoParser.parse(location, proto)
666+
fail("Excepted to fail for edition $edition")
667+
} catch (e: IllegalStateException) {
668+
assertThat(e).hasMessage("Syntax error in file.proto:1:1: unknown edition: 2025")
669+
}
670+
}
671+
}
672+
630673
@Test
631674
fun syntaxSpecified() {
632675
val proto = """
@@ -671,7 +714,7 @@ class ProtoParserTest {
671714
fail()
672715
} catch (expected: IllegalStateException) {
673716
assertThat(expected).hasMessage(
674-
"Syntax error in file.proto:2:1: 'syntax' element must be the first declaration in a file",
717+
"Syntax error in file.proto:2:1: 'syntax' or 'edition' element must be the first declaration in a file",
675718
)
676719
}
677720
}
@@ -3174,7 +3217,7 @@ class ProtoParserTest {
31743217
fail()
31753218
} catch (expected: IllegalStateException) {
31763219
assertThat(expected)
3177-
.messageContains("Syntax error in file.proto:2:3: too many syntax definitions")
3220+
.messageContains("Syntax error in file.proto:2:3: too many syntax or edition definitions")
31783221
}
31793222
}
31803223

wire-swift-generator/src/main/java/com/squareup/wire/swift/SwiftGenerator.kt

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package com.squareup.wire.swift
1717

18+
import com.squareup.wire.Syntax.Edition
1819
import com.squareup.wire.Syntax.PROTO_2
1920
import com.squareup.wire.Syntax.PROTO_3
2021
import com.squareup.wire.internal.camelCase
@@ -601,7 +602,9 @@ class SwiftGenerator private constructor(
601602
// Declare locals into which everything is written before promoting to members.
602603
type.declaredFields.forEach { field ->
603604
val localType = when (type.syntax) {
604-
PROTO_2 -> if (field.isRepeated || field.isMap) {
605+
is Edition,
606+
PROTO_2,
607+
-> if (field.isRepeated || field.isMap) {
605608
field.typeName
606609
} else {
607610
field.typeName.makeOptional()
@@ -614,7 +617,9 @@ class SwiftGenerator private constructor(
614617
}
615618

616619
val initializer = when (type.syntax) {
617-
PROTO_2 -> when {
620+
is Edition,
621+
PROTO_2,
622+
-> when {
618623
field.isMap -> "[:]"
619624
field.isRepeated -> "[]"
620625
else -> "nil"
@@ -696,7 +701,9 @@ class SwiftGenerator private constructor(
696701
val hasPropertyWrapper = !isIndirect(type, field) && (field.defaultedValue != null || field.isProtoDefaulted)
697702

698703
val initializer = when (type.syntax) {
699-
PROTO_2 -> if (field.isOptional || field.isRepeated || field.isMap) {
704+
is Edition,
705+
PROTO_2,
706+
-> if (field.isOptional || field.isRepeated || field.isMap) {
700707
CodeBlock.of("%N", field.safeName)
701708
} else {
702709
CodeBlock.of("try %1T.checkIfMissing(%2N, %3S)", structType, field.safeName, field.name)
@@ -780,13 +787,17 @@ class SwiftGenerator private constructor(
780787

781788
private val MessageType.protoCodableType: DeclaredTypeName
782789
get() = when (syntax) {
783-
PROTO_2 -> proto2Codable
790+
is Edition,
791+
PROTO_2,
792+
-> proto2Codable
784793
PROTO_3 -> proto3Codable
785794
}
786795

787796
private val EnumType.protoCodableType: DeclaredTypeName
788797
get() = when (syntax) {
789-
PROTO_2 -> proto2Enum
798+
is Edition,
799+
PROTO_2,
800+
-> proto2Enum
790801
PROTO_3 -> proto3Enum
791802
}
792803

0 commit comments

Comments
 (0)