Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,22 @@ public open class CodeGenerator(
line()
// Only mark top-level classes for export, internal classes will be exported transitively
if (!nested) line("@pbandk.Export")
if (type.deprecated) line("@Deprecated(message = \"Enum marked deprecated in ${file.name}\")")
// Enums are sealed classes w/ a value and a name, and a companion object with all values
line("$visibility sealed class ${type.kotlinTypeName}(override val value: Int, override val name: String? = null) : pbandk.Message.Enum {")
.indented {
line("override fun equals(other: kotlin.Any?): Boolean = other is ${file.kotlinPackageName}.${type.kotlinFullTypeName} && other.value == value")
line("override fun hashCode(): Int = value.hashCode()")
line("override fun toString(): String = \"${type.kotlinFullTypeName}.\${name ?: \"UNRECOGNIZED\"}(value=\$value)\"")
line()
type.values.forEach { line("$visibility object ${it.kotlinValueTypeName} : ${type.kotlinTypeName}(${it.number}, \"${it.name}\")") }
type.values.forEach {
if (it.deprecated) line("@Deprecated(message = \"Enum value marked deprecated in ${file.name}\")")
line("$visibility object ${it.kotlinValueTypeName} : ${type.kotlinTypeName}(${it.number}, \"${it.name}\")")
}
line("$visibility class UNRECOGNIZED(value: Int) : ${type.kotlinTypeName}(value)")
line()
line("$visibility companion object : pbandk.Message.Enum.Companion<${file.kotlinPackageName}.${type.kotlinFullTypeName}> {").indented {
if (type.deprecated || type.values.any { it.deprecated }) line("@Suppress(\"DEPRECATION\")")
line("$visibility val values: List<${file.kotlinPackageName}.${type.kotlinFullTypeName}> by lazy { listOf(${type.values.joinToString(", ") { it.kotlinValueTypeName }}) }")
line("override fun fromValue(value: Int): ${file.kotlinPackageName}.${type.kotlinFullTypeName} = values.firstOrNull { it.value == value } ?: UNRECOGNIZED(value)")
line("override fun fromName(name: String): ${file.kotlinPackageName}.${type.kotlinFullTypeName} = values.firstOrNull { it.name == name } ?: throw IllegalArgumentException(\"No ${type.kotlinTypeName} with name: \$name\")")
Expand All @@ -104,6 +109,7 @@ public open class CodeGenerator(
line()
// Only mark top-level classes for export, internal classes will be exported transitively
if (!nested) line("@pbandk.Export")
if (type.deprecated) line("@Deprecated(message = \"Message marked deprecated in ${file.name}\")")
line("$visibility data class ${type.kotlinTypeName}(").indented {
val fieldBegin = if (type.mapEntry) "override " else ""
type.fields.forEach { field ->
Expand Down Expand Up @@ -298,6 +304,7 @@ public open class CodeGenerator(
// have to get rid of (i.e. `Person.AddressBook` becomes `PersonAddressBook`).
line("@pbandk.Export")
line("@pbandk.JsName(\"orDefaultFor${type.kotlinFullTypeName.replace(".", "")}\")")
if (type.deprecated) line("@Suppress(\"DEPRECATION\")")
line("$visibility fun ${type.kotlinFullTypeName}?.orDefault(): ${type.kotlinTypeNameWithPackage} = this ?: ${type.kotlinFullTypeName}.defaultInstance")
}

Expand Down Expand Up @@ -355,8 +362,9 @@ public open class CodeGenerator(
}

line()
if (type.deprecated) line("@Suppress(\"DEPRECATION\")")
line("private fun ${type.kotlinFullTypeName}.protoMergeImpl(plus: pbandk.Message?): ${type.kotlinFullTypeName} = (plus as? ${type.kotlinFullTypeName})?.let {").indented {
if (type.sortedStandardFieldsWithOneOfs().any { it.first.options.deprecated == true }) {
if (!type.deprecated && type.sortedStandardFieldsWithOneOfs().any { it.first.options.deprecated == true }) {
line("@Suppress(\"DEPRECATION\")")
}
line("it.copy(").indented {
Expand All @@ -376,7 +384,8 @@ public open class CodeGenerator(
protected fun writeMessageDecodeWithExtension(type: File.Type.Message) {
val lineStr = "private fun ${type.kotlinFullTypeName}.Companion." +
"decodeWithImpl(u: pbandk.MessageDecoder): ${type.kotlinFullTypeName} {"
line().line("@Suppress(\"UNCHECKED_CAST\")").line(lineStr).indented {
val suppressAnnotation = if (type.deprecated) "@Suppress(\"UNCHECKED_CAST\", \"DEPRECATION\")" else "@Suppress(\"UNCHECKED_CAST\")"
line().line(suppressAnnotation).line(lineStr).indented {
// A bunch of locals for each field, initialized with defaults
val doneKotlinFields = type.fields.map {
when (it) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,19 @@ public data class File(
val mapEntry: Boolean,
override val kotlinTypeName: String,
override val kotlinFullTypeName: String,
val extensionRange: List<pbandk.wkt.DescriptorProto.ExtensionRange> = emptyList()
val extensionRange: List<pbandk.wkt.DescriptorProto.ExtensionRange> = emptyList(),
val deprecated: Boolean = false
) : Type()

public data class Enum(
override val name: String,
override val fullName: String,
val values: List<Value>,
override val kotlinTypeName: String,
override val kotlinFullTypeName: String
override val kotlinFullTypeName: String,
val deprecated: Boolean = false
) : Type() {
public data class Value(val number: Int, val name: String, val kotlinValueTypeName: String)
public data class Value(val number: Int, val name: String, val kotlinValueTypeName: String, val deprecated: Boolean = false)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ internal open class FileBuilder(val namer: Namer = Namer.Standard, val supportMa
kotlinValueTypeName = namer.newEnumValueTypeName(
enumDesc.name!!,
value.name!!,
values.map { it.kotlinValueTypeName })
values.map { it.kotlinValueTypeName }),
deprecated = value.options?.deprecated == true
)
},
kotlinTypeName = kotlinTypeName,
kotlinFullTypeName = parentKotlinFullName?.let { "${it}." }.orEmpty() + kotlinTypeName,
deprecated = enumDesc.options?.deprecated == true
)
}

Expand Down Expand Up @@ -94,7 +96,8 @@ internal open class FileBuilder(val namer: Namer = Namer.Standard, val supportMa
mapEntry = supportMaps && msgDesc.options?.mapEntry == true,
kotlinTypeName = kotlinTypeName,
kotlinFullTypeName = kotlinFullTypeName,
extensionRange = msgDesc.extensionRange
extensionRange = msgDesc.extensionRange,
deprecated = msgDesc.options?.deprecated == true
)
}

Expand Down
33 changes: 33 additions & 0 deletions protoc-gen-pbandk/lib/src/jvmTest/kotlin/CodeGeneratorTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,39 @@ class CodeGeneratorTest {
assertTrue(deprecatedField.hasAnnotation<Deprecated>())
}

@Test
fun testDeprecatedMessage() {
val result = compileProto("options.proto")

assertEquals(ExitCode.OK, result.exitCode, result.messages)

val deprecatedMessageClazz = result.classLoader.loadClass("foobar.DeprecatedMessage").kotlin
assertTrue(deprecatedMessageClazz.hasAnnotation<Deprecated>())
}

@Test
fun testDeprecatedEnum() {
val result = compileProto("options.proto")

assertEquals(ExitCode.OK, result.exitCode, result.messages)

val deprecatedEnumClazz = result.classLoader.loadClass("foobar.DeprecatedEnum").kotlin
assertTrue(deprecatedEnumClazz.hasAnnotation<Deprecated>())
}

@Test
fun testDeprecatedEnumValue() {
val result = compileProto("options.proto")

assertEquals(ExitCode.OK, result.exitCode, result.messages)

val enumClazz = result.classLoader.loadClass("foobar.EnumWithDeprecatedValue").kotlin
val deprecatedValue = enumClazz.sealedSubclasses.single { it.simpleName == "OLD" }
assertTrue(deprecatedValue.hasAnnotation<Deprecated>())
val newValue = enumClazz.sealedSubclasses.single { it.simpleName == "NEW" }
assertFalse(newValue.hasAnnotation<Deprecated>())
}

@Test
fun testOneOf_SameNameField() {
val result = compileProto("oneof_same_name.proto")
Expand Down
17 changes: 17 additions & 0 deletions protoc-gen-pbandk/lib/src/jvmTest/resources/protos/options.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,21 @@ package foobar;

message Foo {
int32 deprecated_field = 1 [deprecated = true];
}

message DeprecatedMessage {
option deprecated = true;
string value = 1;
}

enum DeprecatedEnum {
option deprecated = true;
DEPRECATED_ENUM_UNSPECIFIED = 0;
DEPRECATED_ENUM_VALUE = 1;
}

enum EnumWithDeprecatedValue {
ENUM_WITH_DEPRECATED_VALUE_UNSPECIFIED = 0;
ENUM_WITH_DEPRECATED_VALUE_OLD = 1 [deprecated = true];
ENUM_WITH_DEPRECATED_VALUE_NEW = 2;
}