diff --git a/README.md b/README.md index 88b3a8410..f6348dd1d 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,7 @@ Supports only version 3 of the Protocol Buffers language. ### Features - Idiomatic and concise [Kotlin builder DSL](#generated-code) -- Protokt-specific options: [non-null types (dangerous)](#non-null-fields), -[wrapper types](#wrapper-types), -[interface implementation](#interface-implementation), -and more +- Protokt-specific options: [wrapper types](#wrapper-types), [interface implementation](#interface-implementation), and more - Representation of the well-known types as Kotlin nullable types: `StringValue` is represented as `String?`, etc. - Multiplatform support for Kotlin JS (beta) @@ -635,8 +632,7 @@ dependencies { ``` Wrapper types that wrap protobuf messages are nullable. For example, -`java.time.Instant` wraps the well-known type `google.protobuf.Timestamp`. They -can be made non-nullable by using the non-null option described below. +`java.time.Instant` wraps the well-known type `google.protobuf.Timestamp`. Wrapper types that wrap protobuf primitives, for example `java.util.UUID` which wraps `bytes`, are nullable when they cannot wrap their wrapped type's @@ -658,8 +654,6 @@ google.protobuf.BytesValue nullable_uuid = 3 [ ]; ``` -This behavior can be overridden with the [`non_null` option](#non-null-fields). - Wrapper types can be repeated: ```protobuf @@ -686,41 +680,6 @@ _N.b. Well-known type nullability is implemented with for each message defined in [wrappers.proto](https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/wrappers.proto)._ -### Non-null fields -If a message has no meaning whatsoever when a particular non-scalar field is -missing, you can emulate proto2's `required` key word by using the -`(protokt.v1.property).non_null` option: - -```protobuf -message Sample {} - -message NonNullSampleMessage { - Sample non_null_sample = 1 [ - (protokt.v1.property).non_null = true - ]; -} -``` - -Generated code will not have a nullable type, so the field can be referenced -without using Kotlin's `!!`. - -Oneof fields can also be declared non-null: - -```protobuf -message NonNullSampleMessage { - oneof non_null_oneof { - option (protokt.v1.oneof).non_null = true; - - string message = 1; - } -} -``` - -Note that deserialization of a message with a non-nullable field will fail if the -message being decoded does not contain an instance of the required field. - -This functionality will likely be removed. - ### Interface implementation #### Messages @@ -759,21 +718,19 @@ dependencies { } ``` -Messages can also implement interfaces by delegation to one of their fields; -in this case the delegated interface need not live in a separate project, as -protokt requires no inspection of it: +Messages can also implement interfaces by delegation to one of their fields: ```protobuf message ImplementsWithDelegate { option (protokt.v1.class).implements = "Model2 by modelTwo"; - ImplementsModel2 model_two = 1 [ - (protokt.v1.property).non_null = true - ]; + ImplementsModel2 model_two = 1; } ``` -Note that the `by` clause references the field by its lower camel case name. +Note that the `by` clause references the field by its lower camel case name. +Properties on delegate interfaces must be nullable since message fields themselves +are nullable and may not be present on the wire. #### Oneof Fields @@ -782,7 +739,7 @@ Oneof fields can declare that they implement an interface with the also implement the interface. This allows access of common properties without a `when` statement that always ultimately extracts the same property. -Suppose you have a domain object MyObjectWithConfig that has a non-null configuration +Suppose you have a domain object MyObjectWithConfig that has configuration that specifies a third-party server for communication. For flexibility, this configuration will be modifiable by the server and versioned by a simple integer. To hasten subsequent loading of the configuration, a client may save a resolved @@ -816,7 +773,6 @@ message MyObjectWithConfig { ]; oneof Config { - option (protokt.v1.oneof).non_null = true; option (protokt.v1.oneof).implements = "com.toasttab.example.Config"; ServerSpecified server_specified = 2; @@ -836,9 +792,7 @@ message ServerSpecified { message ClientResolved { option (protokt.v1.class).implements = "com.toasttab.example.Config by config"; - ServerSpecified config = 1 [ - (protokt.v1.property).non_null = true - ]; + ServerSpecified config = 1; bytes last_known_address = 2 [ (protokt.v1.property).wrap = "java.net.InetAddress" @@ -853,7 +807,7 @@ Protokt will generate: public class MyObjectWithConfig private constructor( @GeneratedProperty(1) public val id: UUID?, - public val config: Config, + public val config: Config?, public val unknownFields: UnknownFieldSet = UnknownFieldSet.empty() ) : AbstractMessage() { @@ -906,18 +860,10 @@ accessing the property via a `when` expression: ```kotlin fun printVersion(config: MyObjectWithConfig.Config) { - println(config.version) + println(config?.version) } ``` -This pattern is dangerous: since the oneof must be marked non-nullable, you -cannot compatibly add new implementing fields to a producer before a consumer -is updated with the new generated code. The old consumer will attempt to -deserialize the new field as an unknown field and the non-null assertion on the -oneof field during the constructor call will fail. - -This functionality will likely be removed. - ### BytesSlice When reading messages that contain other serialized messages as `bytes` fields, diff --git a/extensions/protokt-extensions-lite/api/protokt-extensions-lite.api b/extensions/protokt-extensions-lite/api/protokt-extensions-lite.api index e13ac3b11..2835d21ad 100644 --- a/extensions/protokt-extensions-lite/api/protokt-extensions-lite.api +++ b/extensions/protokt-extensions-lite/api/protokt-extensions-lite.api @@ -369,14 +369,13 @@ public final class protokt/v1/EnumValueOptions$Deserializer : protokt/v1/Abstrac public final class protokt/v1/FieldOptions : protokt/v1/AbstractMessage { public static final field Deserializer Lprotokt/v1/FieldOptions$Deserializer; - public synthetic fun (ZLjava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lprotokt/v1/UnknownFieldSet;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lprotokt/v1/UnknownFieldSet;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun copy (Lkotlin/jvm/functions/Function1;)Lprotokt/v1/FieldOptions; public static fun deserialize (Lprotokt/v1/Reader;)Lprotokt/v1/FieldOptions; public fun equals (Ljava/lang/Object;)Z public final fun getBytesSlice ()Z public final fun getDeprecationMessage ()Ljava/lang/String; public final fun getKeyWrap ()Ljava/lang/String; - public final fun getNonNull ()Z public final fun getUnknownFields ()Lprotokt/v1/UnknownFieldSet; public final fun getValueWrap ()Ljava/lang/String; public final fun getWrap ()Ljava/lang/String; @@ -393,14 +392,12 @@ public final class protokt/v1/FieldOptions$Builder { public final fun getBytesSlice ()Z public final fun getDeprecationMessage ()Ljava/lang/String; public final fun getKeyWrap ()Ljava/lang/String; - public final fun getNonNull ()Z public final fun getUnknownFields ()Lprotokt/v1/UnknownFieldSet; public final fun getValueWrap ()Ljava/lang/String; public final fun getWrap ()Ljava/lang/String; public final fun setBytesSlice (Z)V public final fun setDeprecationMessage (Ljava/lang/String;)V public final fun setKeyWrap (Ljava/lang/String;)V - public final fun setNonNull (Z)V public final fun setUnknownFields (Lprotokt/v1/UnknownFieldSet;)V public final fun setValueWrap (Ljava/lang/String;)V public final fun setWrap (Ljava/lang/String;)V @@ -593,13 +590,12 @@ public final class protokt/v1/MethodOptions$Deserializer : protokt/v1/AbstractDe public final class protokt/v1/OneofOptions : protokt/v1/AbstractMessage { public static final field Deserializer Lprotokt/v1/OneofOptions$Deserializer; - public synthetic fun (ZLjava/lang/String;Ljava/lang/String;Lprotokt/v1/UnknownFieldSet;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Lprotokt/v1/UnknownFieldSet;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun copy (Lkotlin/jvm/functions/Function1;)Lprotokt/v1/OneofOptions; public static fun deserialize (Lprotokt/v1/Reader;)Lprotokt/v1/OneofOptions; public fun equals (Ljava/lang/Object;)Z public final fun getDeprecationMessage ()Ljava/lang/String; public final fun getImplements ()Ljava/lang/String; - public final fun getNonNull ()Z public final fun getUnknownFields ()Lprotokt/v1/UnknownFieldSet; public fun hashCode ()I public static final fun invoke (Lkotlin/jvm/functions/Function1;)Lprotokt/v1/OneofOptions; @@ -613,11 +609,9 @@ public final class protokt/v1/OneofOptions$Builder { public final fun build ()Lprotokt/v1/OneofOptions; public final fun getDeprecationMessage ()Ljava/lang/String; public final fun getImplements ()Ljava/lang/String; - public final fun getNonNull ()Z public final fun getUnknownFields ()Lprotokt/v1/UnknownFieldSet; public final fun setDeprecationMessage (Ljava/lang/String;)V public final fun setImplements (Ljava/lang/String;)V - public final fun setNonNull (Z)V public final fun setUnknownFields (Lprotokt/v1/UnknownFieldSet;)V } diff --git a/extensions/protokt-extensions-lite/src/extensions-proto/protokt/v1/protokt.proto b/extensions/protokt-extensions-lite/src/extensions-proto/protokt/v1/protokt.proto index 421693853..c535a0f42 100644 --- a/extensions/protokt-extensions-lite/src/extensions-proto/protokt/v1/protokt.proto +++ b/extensions/protokt-extensions-lite/src/extensions-proto/protokt/v1/protokt.proto @@ -46,18 +46,6 @@ extend google.protobuf.MessageOptions { } message FieldOptions { - // Makes a message-type field non-nullable in the generated Kotlin code. - // Beware that deserialization will NPE if the field is missing from the - // protobuf payload. Adding a non-null field to an existing message is a - // backwards-incompatible change. - // - // For example: - // - // message Foo { - // string id = 1 [(protokt.v1.property).non_null = true]; - // } - bool non_null = 1; - // Expose a wrapper class instead of a raw protobuf type. // // For example: @@ -76,14 +64,14 @@ message FieldOptions { // full qualification is optional. // // This option can be applied to repeated fields. - string wrap = 2; + string wrap = 1; // Maps a bytes field to BytesSlice. If deserialized from a byte array, // BytesSlice will point to the source array without copying the subarray. - bool bytes_slice = 3; + bool bytes_slice = 2; // Provides a message for deprecation - string deprecation_message = 4; + string deprecation_message = 3; // Expose a wrapper class instead of a raw protobuf type for the key type of // a map. @@ -100,7 +88,7 @@ message FieldOptions { // class Foo(val map: Map) ... // // Scoping rules are the same as those for declaring regular field wrapper types. - string key_wrap = 5; + string key_wrap = 4; // Expose a wrapper class instead of a raw protobuf type for the value type of // a map. @@ -117,7 +105,7 @@ message FieldOptions { // class Foo(val map: Map) ... // // Scoping rules are the same as those for declaring regular field wrapper types. - string value_wrap = 6; + string value_wrap = 5; } extend google.protobuf.FieldOptions { @@ -125,30 +113,13 @@ extend google.protobuf.FieldOptions { } message OneofOptions { - // Makes a oneof field non-nullable in generated Kotlin code. Beware that - // deserialization will NPE if the field is missing from the protobuf payload. - // Adding a non-null field to an existing message is a backwards-incompatible - // change. - // - // For example: - // - // message Message { - // oneof some_field_name { - // option (protokt.v1.oneof).non_null = true; - // - // string id = 1; - // } - // } - // - bool non_null = 1; - // Make the sealed class implement an interface, enforcing the presence of a // property in each possible variant. Scoping rules are the same as those // for declaring wrapper types. - string implements = 2; + string implements = 1; // Provides a message for deprecation - string deprecation_message = 3; + string deprecation_message = 2; } extend google.protobuf.OneofOptions { diff --git a/gradle-plugin-integration-test/multiplatform/src/main/proto/testing/test.proto b/gradle-plugin-integration-test/multiplatform/src/main/proto/testing/test.proto index 2e5acc377..24867861e 100644 --- a/gradle-plugin-integration-test/multiplatform/src/main/proto/testing/test.proto +++ b/gradle-plugin-integration-test/multiplatform/src/main/proto/testing/test.proto @@ -34,6 +34,6 @@ message TestMessage { } message Submessage { - TestMessage qux = 1 [(protokt.v1.property).non_null = true]; + TestMessage qux = 1; } } diff --git a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/DeserializerSupport.kt b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/DeserializerSupport.kt index f2b19ffb6..201043e09 100644 --- a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/DeserializerSupport.kt +++ b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/DeserializerSupport.kt @@ -17,7 +17,6 @@ package protokt.v1.codegen.generate import com.squareup.kotlinpoet.CodeBlock import com.squareup.kotlinpoet.buildCodeBlock -import com.squareup.kotlinpoet.withIndent import protokt.v1.reflect.FieldType internal fun deserializeVarInitialState(p: PropertyInfo) = @@ -28,33 +27,15 @@ internal fun deserializeVarInitialState(p: PropertyInfo) = } internal fun wrapDeserializedValueForConstructor(p: PropertyInfo) = - if (p.nonNullOption) { - buildCodeBlock { - beginControlFlow("requireNotNull(%N)", p.name) - add("StringBuilder(\"%N\")\n", p.name) - withIndent { - add( - ( - ".append(\" specified nonnull with (protokt." + - "${if (p.oneof) "oneof" else "property"}).non_null " + - "but was null\")" - ).bindSpaces() - ) - add("\n") - } - endControlFlowWithoutNewline() - } + if (p.isMap) { + CodeBlock.of("%M(%N)", unmodifiableMap, p.name) + } else if (p.repeated) { + CodeBlock.of("%M(%N)", unmodifiableList, p.name) } else { - if (p.isMap) { - CodeBlock.of("%M(%N)", unmodifiableMap, p.name) - } else if (p.repeated) { - CodeBlock.of("%M(%N)", unmodifiableList, p.name) - } else { - buildCodeBlock { - add("%N", p.name) - if (p.wrapped && !p.nullable) { - add(" ?: %L", p.defaultValue) - } + buildCodeBlock { + add("%N", p.name) + if (p.wrapped && !p.nullable) { + add(" ?: %L", p.defaultValue) } } } diff --git a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/Implements.kt b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/Implements.kt index 3ba0b3ef2..ef0bb4605 100644 --- a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/Implements.kt +++ b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/Implements.kt @@ -16,10 +16,15 @@ package protokt.v1.codegen.generate import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.TypeSpec +import com.squareup.kotlinpoet.asTypeName import protokt.v1.codegen.generate.CodeGenerator.Context import protokt.v1.codegen.util.Message import protokt.v1.codegen.util.StandardField +import kotlin.reflect.KClass internal object Implements { fun StandardField.overrides( @@ -27,7 +32,7 @@ internal object Implements { msg: Message ) = msg.superInterface(ctx) - ?.let { fieldName in ctx.info.context.classLookup.properties(it.canonicalName) } + ?.let { fieldName in ctx.info.context.classLookup.properties(it.canonicalName).map { p -> p.name } } ?: false fun TypeSpec.Builder.handleSuperInterface(implements: ClassName?, v: OneofGeneratorInfo? = null) = @@ -43,27 +48,66 @@ internal object Implements { fun TypeSpec.Builder.handleSuperInterface(msg: Message, ctx: Context) = apply { - if (msg.options.protokt.implements.isNotEmpty()) { - if (msg.options.protokt.implements.delegates()) { - addSuperinterface( - ClassName.bestGuess(msg.options.protokt.implements.substringBefore(" by ")), - msg.options.protokt.implements.substringAfter(" by ") - ) - } else { - addSuperinterface(msg.superInterface(ctx)!!) + val superInterface = msg.superInterface(ctx) + if (superInterface != null) { + addSuperinterface(superInterface.`interface`) + if (superInterface.delegate != null) { + // can't actually delegate because message types are nullable + delegateProperties(msg, ctx, superInterface.canonicalName, superInterface.delegate) } } } - private fun String.delegates() = - contains(" by ") + private fun TypeSpec.Builder.delegateProperties(msg: Message, ctx: Context, canonicalName: String, fieldName: String) { + val fieldsByName = msg.fields.filterIsInstance().associateBy { it.fieldName } + val interfaceFields = + ctx.info.context.classLookup + .properties(canonicalName) + .associateBy { it.name } + + val implementFields = interfaceFields.values.filter { it.name !in fieldsByName.keys } - private fun Message.superInterface(ctx: Context) = - options.protokt.implements.let { - if (it.isNotEmpty() && !it.delegates()) { - inferClassName(it, ctx) - } else { - null + implementFields.forEach { + require(it.returnType.isMarkedNullable) { + "Delegated properties must be nullable because message types are nullable; " + + "property ${it.name} is non-nullable" } } + + implementFields.forEach { + addProperty( + PropertySpec.builder( + it.name, + (it.returnType.classifier as KClass<*>).asTypeName().copy(nullable = true) + ) + .addModifiers(KModifier.OVERRIDE) + .getter( + FunSpec.getterBuilder() + .addCode("return %L?.%L", fieldName, it.name) + .build() + ) + .build() + ) + } + } + + private class SuperInterface( + val `interface`: ClassName, + val delegate: String? + ) { + val canonicalName = `interface`.canonicalName + } + + private fun Message.superInterface(ctx: Context): SuperInterface? { + val implements = options.protokt.implements + return when { + implements.isEmpty() -> null + implements.contains(" by ") -> + SuperInterface( + inferClassName(implements.substringBefore(" by "), ctx), + implements.substringAfter(" by ") + ) + else -> SuperInterface(inferClassName(implements, ctx), null) + } + } } diff --git a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/MessageSizeGenerator.kt b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/MessageSizeGenerator.kt index c30a97008..991db1986 100644 --- a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/MessageSizeGenerator.kt +++ b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/MessageSizeGenerator.kt @@ -20,7 +20,6 @@ import com.squareup.kotlinpoet.KModifier import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.buildCodeBlock import protokt.v1.codegen.generate.CodeGenerator.Context -import protokt.v1.codegen.generate.Nullability.hasNonNullOption import protokt.v1.codegen.generate.Wrapper.interceptFieldSizeof import protokt.v1.codegen.generate.Wrapper.interceptSizeof import protokt.v1.codegen.generate.Wrapper.interceptValueAccess @@ -56,12 +55,7 @@ private class MessageSizeGenerator( properties, false, { std, _ -> CodeBlock.of("$resultVarName·+=·%L", sizeOf(std, ctx)) }, - { oneof, std, _ -> sizeofOneof(oneof, std) }, - { - if (it.hasNonNullOption) { - add("$resultVarName·+=·") - } - } + { oneof, std, _ -> sizeofOneof(oneof, std) } ) return PropertySpec.builder(MESSAGE_SIZE, Int::class) @@ -88,21 +82,18 @@ private class MessageSizeGenerator( } private fun sizeofOneof(o: Oneof, f: StandardField) = - sizeOf( - f, - ctx, - interceptSizeof( + CodeBlock.of( + "$resultVarName·+=·%L", + sizeOf( f, - CodeBlock.of("%N.%N", o.fieldName, f.fieldName), - ctx + ctx, + interceptSizeof( + f, + CodeBlock.of("%N.%N", o.fieldName, f.fieldName), + ctx + ) ) - ).let { s -> - if (o.hasNonNullOption) { - s - } else { - CodeBlock.of("$resultVarName·+=·%L", s) - } - } + ) } internal fun sizeOf( diff --git a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/Nullability.kt b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/Nullability.kt index 48110e48c..fea49c911 100644 --- a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/Nullability.kt +++ b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/Nullability.kt @@ -23,18 +23,7 @@ import protokt.v1.codegen.util.StandardField import protokt.v1.reflect.FieldType internal object Nullability { - val Field.hasNonNullOption - get() = - when (this) { - is StandardField -> options.protokt.nonNull - is Oneof -> options.protokt.nonNull - } - val Field.nullable - get() = - isKotlinRepresentationNullable && !hasNonNullOption - - private val Field.isKotlinRepresentationNullable get() = when (this) { is StandardField -> (type == FieldType.Message && !repeated) || optional @@ -47,13 +36,6 @@ internal object Nullability { !repeated && type !in setOf(FieldType.Message, FieldType.Enum) - fun propertyType(o: Oneof) = - if (o.hasNonNullOption) { - o.className - } else { - o.className.copy(nullable = true) - } - fun propertyType(f: StandardField, type: TypeName, wrapperRequiresNullability: Boolean) = if (f.nullable || wrapperRequiresNullability) { type.copy(nullable = true) @@ -65,7 +47,6 @@ internal object Nullability { if ( f.repeated || f.nullable || - f.isKotlinRepresentationNullable || f.isWrappedNonRepeatedPrimitive ) { type.copy(nullable = true) @@ -74,10 +55,7 @@ internal object Nullability { } fun dslPropertyType(f: StandardField, type: TypeName) = - if ( - f.isKotlinRepresentationNullable || - f.isWrappedNonRepeatedPrimitive - ) { + if (f.nullable || f.isWrappedNonRepeatedPrimitive) { type.copy(nullable = true) } else { type diff --git a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/PropertyAnnotator.kt b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/PropertyAnnotator.kt index 3f4aca019..d53de3ac4 100644 --- a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/PropertyAnnotator.kt +++ b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/PropertyAnnotator.kt @@ -24,7 +24,6 @@ import protokt.v1.codegen.generate.Deprecation.renderOptions import protokt.v1.codegen.generate.Implements.overrides import protokt.v1.codegen.generate.Nullability.deserializeType import protokt.v1.codegen.generate.Nullability.dslPropertyType -import protokt.v1.codegen.generate.Nullability.hasNonNullOption import protokt.v1.codegen.generate.Nullability.nullable import protokt.v1.codegen.generate.Nullability.propertyType import protokt.v1.codegen.generate.Wrapper.interceptDefaultValue @@ -55,19 +54,17 @@ private class PropertyAnnotator( return when (field) { is StandardField -> { annotateStandard(field).let { type -> - val wrapperRequiresNullability = field.wrapperRequiresNullability(ctx) PropertyInfo( name = field.fieldName, number = field.number, - propertyType = propertyType(field, type, wrapperRequiresNullability), + propertyType = propertyType(field, type, field.wrapperRequiresNullability(ctx)), deserializeType = deserializeType(field, type), builderPropertyType = dslPropertyType(field, type), defaultValue = field.defaultValue(ctx, msg.mapEntry), fieldType = field.type, repeated = field.repeated, mapEntry = field.mapEntry, - nullable = field.nullable || field.optional || wrapperRequiresNullability, - nonNullOption = field.hasNonNullOption, + nullable = field.nullable || field.optional, overrides = field.overrides(ctx, msg), wrapped = field.wrapped, documentation = documentation, @@ -78,13 +75,12 @@ private class PropertyAnnotator( is Oneof -> PropertyInfo( name = field.fieldName, - propertyType = propertyType(field), + propertyType = field.className.copy(nullable = true), deserializeType = field.className.copy(nullable = true), builderPropertyType = field.className.copy(nullable = true), defaultValue = field.defaultValue(ctx, false), oneof = true, nullable = field.nullable, - nonNullOption = field.hasNonNullOption, documentation = documentation ) } @@ -141,7 +137,6 @@ internal class PropertyInfo( val builderPropertyType: TypeName, val defaultValue: CodeBlock, val nullable: Boolean, - val nonNullOption: Boolean, val fieldType: FieldType? = null, val repeated: Boolean = false, val mapEntry: Message? = null, diff --git a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/SerializeAndSizeSupport.kt b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/SerializeAndSizeSupport.kt index 9f11295f2..406f10e51 100644 --- a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/SerializeAndSizeSupport.kt +++ b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/SerializeAndSizeSupport.kt @@ -19,7 +19,6 @@ import com.squareup.kotlinpoet.CodeBlock import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.buildCodeBlock import protokt.v1.codegen.generate.CodeGenerator.Context -import protokt.v1.codegen.generate.Nullability.hasNonNullOption import protokt.v1.codegen.generate.Nullability.nullable import protokt.v1.codegen.generate.Wrapper.interceptValueAccess import protokt.v1.codegen.generate.Wrapper.wrapperRequiresNullability @@ -34,8 +33,7 @@ internal fun Message.mapFields( properties: List, skipConditionalForUnpackedRepeatedFields: Boolean, std: (StandardField, PropertySpec) -> CodeBlock, - oneof: (Oneof, StandardField, PropertySpec) -> CodeBlock, - oneofPreControlFlow: CodeBlock.Builder.(Oneof) -> Unit = {} + oneof: (Oneof, StandardField, PropertySpec) -> CodeBlock ): List = fields.zip(properties) .map { (field, property) -> @@ -43,7 +41,7 @@ internal fun Message.mapFields( is StandardField -> standardFieldExecution(ctx, field, skipConditionalForUnpackedRepeatedFields) { std(field, property) } is Oneof -> - oneofFieldExecution(field, { oneof(field, it, property) }, oneofPreControlFlow) + oneofFieldExecution(field) { oneof(field, it, property) } } } @@ -58,18 +56,14 @@ private fun standardFieldExecution( add("\n") } - return if (field.hasNonNullOption) { - buildCodeBlock { addStmt() } - } else { - buildCodeBlock { - if (field.repeated && !field.packed && skipConditional) { - // skip isNotEmpty check when not packed; will short circuit correctly - addStmt() - } else { - beginControlFlow("if (%L)", field.nonDefault(ctx)) - addStmt() - endControlFlow() - } + return buildCodeBlock { + if (field.repeated && !field.packed && skipConditional) { + // skip isNotEmpty check when not packed; will short circuit correctly + addStmt() + } else { + beginControlFlow("if (%L)", field.nonDefault(ctx)) + addStmt() + endControlFlow() } } } @@ -97,11 +91,9 @@ private fun StandardField.nonDefault(ctx: Context): CodeBlock { private fun oneofFieldExecution( field: Oneof, - stmt: (StandardField) -> CodeBlock, - preControlFlow: CodeBlock.Builder.(Oneof) -> Unit + stmt: (StandardField) -> CodeBlock ): CodeBlock = buildCodeBlock { - preControlFlow(field) beginControlFlow("when (%N)", field.fieldName) oneofInstanceConditionals(field) { stmt(it) }.forEach(::add) endControlFlow() @@ -114,14 +106,7 @@ private fun oneofInstanceConditionals(f: Oneof, stmt: (StandardField) -> CodeBlo buildCodeBlock { addStatement("is·%T·->\n%L", f.qualify(it), stmt(it)) } - } - .let { - if (f.hasNonNullOption) { - it - } else { - it + buildCodeBlock { addStatement("null·->·Unit") } - } - } + } + buildCodeBlock { addStatement("null·->·Unit") } internal fun Oneof.qualify(f: StandardField) = className.nestedClass(fieldTypeNames.getValue(f.fieldName)) diff --git a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/Wrapper.kt b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/Wrapper.kt index 528e4b322..dab259aa8 100644 --- a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/Wrapper.kt +++ b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/generate/Wrapper.kt @@ -24,7 +24,6 @@ import protokt.v1.BytesSlice import protokt.v1.Converter import protokt.v1.OptimizedSizeOfConverter import protokt.v1.codegen.generate.CodeGenerator.Context -import protokt.v1.codegen.generate.Nullability.hasNonNullOption import protokt.v1.codegen.util.GeneratorContext import protokt.v1.codegen.util.StandardField import protokt.v1.reflect.ClassLookup @@ -39,7 +38,7 @@ internal object Wrapper { get() = wrapWithWellKnownInterception(options.wrap, protoTypeName) != null fun StandardField.wrapperRequiresNullability(ctx: Context) = - wrapperRequiresNonNullOptionForNonNullity(ctx.info.context) && !hasNonNullOption + wrapperRequiresNonNullOptionForNonNullity(ctx.info.context) fun StandardField.wrapperRequiresNonNullOptionForNonNullity(ctx: GeneratorContext) = wrapped && withWrapper(ctx) { it.cannotDeserializeDefaultValue && !repeated } ?: false diff --git a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/util/FieldParser.kt b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/util/FieldParser.kt index b27f40680..361c2d651 100644 --- a/protokt-codegen/src/main/kotlin/protokt/v1/codegen/util/FieldParser.kt +++ b/protokt-codegen/src/main/kotlin/protokt/v1/codegen/util/FieldParser.kt @@ -27,7 +27,6 @@ import com.google.protobuf.DescriptorProtos.FileDescriptorProto import com.google.protobuf.DescriptorProtos.OneofDescriptorProto import com.squareup.kotlinpoet.ClassName import com.toasttab.protokt.v1.ProtoktProtos -import protokt.v1.codegen.generate.Wrapper.wrapperRequiresNonNullOptionForNonNullity import protokt.v1.codegen.util.ErrorContext.withFieldName import protokt.v1.reflect.FieldType import protokt.v1.reflect.typeName @@ -153,10 +152,6 @@ internal class FieldParser( index = idx ) - if (protoktOptions.nonNull) { - validateNonNullOption(fdp, result, withinOneof, optional) - } - return result } @@ -220,51 +215,4 @@ internal class FieldParser( // the default value for an unset boolean is false. (ctx.proto3 && (!fdp.options.hasPacked() || (fdp.options.hasPacked() && fdp.options.packed))) ) - - private fun validateNonNullOption( - fdp: FieldDescriptorProto, - field: StandardField, - withinOneof: Boolean, - optional: Boolean - ) { - fun FieldType.typeName() = - this::class.simpleName!!.lowercase() - - fun name(field: StandardField) = - if (field.type == FieldType.Enum) { - field.protoTypeName - } else { - field.type.typeName() - } - - val typeName = - when (field.type) { - FieldType.Enum, FieldType.Message -> fdp.typeName - else -> field.type.typeName() - } - - require(!optional) { - "(protokt.property).non_null is not applicable to optional fields " + - "and is inapplicable to optional $typeName" - } - require(!withinOneof) { - "(protokt.property).non_null is only applicable to top level types " + - "and is inapplicable to oneof field $typeName" - } - - require((field.type == FieldType.Message && !field.repeated) || field.wrapperRequiresNonNullOptionForNonNullity(ctx)) { - "(protokt.property).non_null is only applicable to message types " + - "and is inapplicable to non-message " + - when { - field.mapEntry != null -> - "map<${name(field.mapKey)}, ${name(field.mapValue)}>" - - field.repeated -> - "repeated $typeName" - - else -> - field.type.typeName() - } - } - } } diff --git a/protokt-codegen/src/test/kotlin/protokt/v1/codegen/ImplementDelegationTest.kt b/protokt-codegen/src/test/kotlin/protokt/v1/codegen/ImplementDelegationTest.kt new file mode 100644 index 000000000..b44e21209 --- /dev/null +++ b/protokt-codegen/src/test/kotlin/protokt/v1/codegen/ImplementDelegationTest.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Toast, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package protokt.v1.codegen + +import com.google.common.truth.Truth.assertThat +import org.junit.jupiter.api.Test + +class ImplementDelegationTest : AbstractProtoktCodegenTest() { + @Test + fun `delegate to interface with non-null property`() { + val result = runPlugin("implement_by_delegate_with_non_null_property.proto") as Failure + + assertThat(result.err) + .contains("Delegated properties must be nullable because message types are nullable; property id is non-nullable") + } +} + +@Suppress("UNUSED") +interface Model { + val id: String +} diff --git a/protokt-codegen/src/test/kotlin/protokt/v1/codegen/NonNullValidationTest.kt b/protokt-codegen/src/test/kotlin/protokt/v1/codegen/NonNullValidationTest.kt deleted file mode 100644 index 5f2b694ea..000000000 --- a/protokt-codegen/src/test/kotlin/protokt/v1/codegen/NonNullValidationTest.kt +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2022 Toast, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package protokt.v1.codegen - -import com.google.common.truth.Truth -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.Arguments -import org.junit.jupiter.params.provider.MethodSource -import protokt.v1.reflect.FieldType - -class NonNullValidationTest : AbstractProtoktCodegenTest() { - @ParameterizedTest - @MethodSource("fieldTypes") - fun `field with bad non-null option`(fieldType: String, fieldTypeName: String?) { - assertFailure( - "non_null.proto", - fieldType, - "Error generating code for file test_file.proto: message TestMessageWithBadNonNullField, field value", - "java.lang.IllegalArgumentException: (protokt.property).non_null is only applicable to message types " + - "and is inapplicable to non-message " + (fieldTypeName ?: fieldType) - ) - } - - @ParameterizedTest - @MethodSource("fieldTypes") - fun `field in nested message with bad non-null option`(fieldType: String, fieldTypeName: String?) { - assertFailure( - "non_null_nested.proto", - fieldType, - "Error generating code for file test_file.proto: message Outer.TestNestedMessageWithBadNonNullField, field value", - "java.lang.IllegalArgumentException: (protokt.property).non_null is only applicable to message types " + - "and is inapplicable to non-message " + (fieldTypeName ?: fieldType) - ) - } - - @ParameterizedTest - @MethodSource("fieldTypesOneof") - fun `oneof type`(fieldType: String, fieldTypeName: String?) { - assertFailure( - "non_null_oneof.proto", - fieldType, - "Error generating code for file test_file.proto: message TestMessageWithBadNonNullOneof, field bar", - "java.lang.IllegalArgumentException: (protokt.property).non_null is only applicable to top level types " + - "and is inapplicable to oneof field " + (fieldTypeName ?: fieldType) - ) - } - - @ParameterizedTest - @MethodSource("fieldTypesOptional") - fun `optional field`(fieldType: String, fieldTypeName: String?) { - assertFailure( - "non_null_optional.proto", - fieldType, - "Error generating code for file test_file.proto: message TestMessageWithBadNonNullOptionalField, field value", - "java.lang.IllegalArgumentException: (protokt.property).non_null is not applicable to optional fields " + - "and is inapplicable to optional " + (fieldTypeName ?: fieldType) - ) - } - - private fun assertFailure( - fileName: String, - fieldType: String, - line0: String, - line1: String - ) { - val result = runPlugin(fileName) { replace("REPLACE", fieldType) } as Failure - - println(result.err) - - Truth.assertThat(result.exitCode).isEqualTo(-1) - Truth.assertThat(result.err.lines()[0]).isEqualTo(line0) - Truth.assertThat(result.err.lines()[1]).isEqualTo(line1) - } - - companion object { - @JvmStatic - fun fieldTypes() = - mapToArgs(argLists()) - - private fun mapToArgs(list: List<*>) = - list.map { - if (it is String) { - Arguments.of(it, null) - } else { - Arguments.of(*(it as List<*>).toTypedArray()) - } - } - - private fun argLists() = - ineligibleAnonymousTypes().map { it.simpleName!!.lowercase() } + - ineligibleAnonymousTypes().map { "repeated ${it.simpleName!!.lowercase()}" } + - listOf( - listOf("Foo", "enum"), - listOf("repeated Foo", "repeated .toasttab.protokt.v1.codegen.testing.Foo"), - listOf("repeated Bar", "repeated .toasttab.protokt.v1.codegen.testing.Bar"), - listOf("map", "map") - ) - - @JvmStatic - fun fieldTypesOptional() = - mapToArgs(argListsOptional()) - - private fun argListsOptional() = - ineligibleAnonymousTypes().map { it.simpleName!!.lowercase() } + - listOf( - listOf("Foo", ".toasttab.protokt.v1.codegen.testing.Foo"), - listOf("Bar", ".toasttab.protokt.v1.codegen.testing.Bar") - ) - - @JvmStatic - fun fieldTypesOneof() = - mapToArgs(argListsOneof()) - - private fun argListsOneof() = - ineligibleAnonymousTypes().map { it.simpleName!!.lowercase() } + - listOf( - listOf("Foo", ".toasttab.protokt.v1.codegen.testing.Foo"), - listOf("Bar", ".toasttab.protokt.v1.codegen.testing.Bar") - ) - - private fun ineligibleAnonymousTypes() = - FieldType::class - .sealedSubclasses - .flatMap { it.sealedSubclasses } - .filterNot { it in setOf(FieldType.Message::class, FieldType.Enum::class) } - } -} diff --git a/protokt-codegen/src/test/resources/non_null.proto b/protokt-codegen/src/test/resources/implement_by_delegate_with_non_null_property.proto similarity index 69% rename from protokt-codegen/src/test/resources/non_null.proto rename to protokt-codegen/src/test/resources/implement_by_delegate_with_non_null_property.proto index ff5818170..df5242907 100644 --- a/protokt-codegen/src/test/resources/non_null.proto +++ b/protokt-codegen/src/test/resources/implement_by_delegate_with_non_null_property.proto @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Toast, Inc. + * Copyright (c) 2023 Toast, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,14 +19,14 @@ package toasttab.protokt.v1.codegen.testing; import "protokt/v1/protokt.proto"; -message TestMessageWithBadNonNullField { - REPLACE value = 1 [ - (.protokt.v1.property).non_null = true - ]; -} +message ImplementsModel { + option (.protokt.v1.class).implements = "protokt.v1.codegen.Model"; -enum Foo { - FOO = 0; + string id = 1; } -message Bar {} +message ImplementsWithDelegate { + option (.protokt.v1.class).implements = "protokt.v1.codegen.Model by model"; + + ImplementsModel model = 1; +} diff --git a/protokt-codegen/src/test/resources/non_null_nested.proto b/protokt-codegen/src/test/resources/non_null_nested.proto deleted file mode 100644 index 0253a4979..000000000 --- a/protokt-codegen/src/test/resources/non_null_nested.proto +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2022 Toast, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto3"; - -package toasttab.protokt.v1.codegen.testing; - -import "protokt/v1/protokt.proto"; - -message Outer { - message TestNestedMessageWithBadNonNullField { - REPLACE value = 1 [ - (.protokt.v1.property).non_null = true - ]; - } -} - -enum Foo { - FOO = 0; -} - -message Bar {} diff --git a/protokt-codegen/src/test/resources/non_null_oneof.proto b/protokt-codegen/src/test/resources/non_null_oneof.proto deleted file mode 100644 index a0a2f5589..000000000 --- a/protokt-codegen/src/test/resources/non_null_oneof.proto +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2022 Toast, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto3"; - -package toasttab.protokt.v1.codegen.testing; - -import "protokt/v1/protokt.proto"; - -message TestMessageWithBadNonNullOneof { - oneof foo { - REPLACE bar = 1 [(.protokt.v1.property).non_null = true]; - } -} - -message Foo {} - -enum Bar { - BAR = 0; -} diff --git a/protokt-codegen/src/test/resources/non_null_optional.proto b/protokt-codegen/src/test/resources/non_null_optional.proto deleted file mode 100644 index 00c73ac39..000000000 --- a/protokt-codegen/src/test/resources/non_null_optional.proto +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2022 Toast, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package toasttab.protokt.v1.codegen.testing; - -import "protokt/v1/protokt.proto"; - -message TestMessageWithBadNonNullOptionalField { - optional REPLACE value = 1 [ - (.protokt.v1.property).non_null = true - ]; -} - -enum Foo { - FOO = 0; -} - -message Bar {} diff --git a/protokt-reflect/api/protokt-reflect.api b/protokt-reflect/api/protokt-reflect.api index d951bdb00..1c2211833 100644 --- a/protokt-reflect/api/protokt-reflect.api +++ b/protokt-reflect/api/protokt-reflect.api @@ -240,7 +240,6 @@ public final class com/toasttab/protokt/v1/ProtoktProtos$FieldOptions : com/goog public static final field BYTES_SLICE_FIELD_NUMBER I public static final field DEPRECATION_MESSAGE_FIELD_NUMBER I public static final field KEY_WRAP_FIELD_NUMBER I - public static final field NON_NULL_FIELD_NUMBER I public static final field VALUE_WRAP_FIELD_NUMBER I public static final field WRAP_FIELD_NUMBER I public fun equals (Ljava/lang/Object;)Z @@ -254,7 +253,6 @@ public final class com/toasttab/protokt/v1/ProtoktProtos$FieldOptions : com/goog public static final fun getDescriptor ()Lcom/google/protobuf/Descriptors$Descriptor; public fun getKeyWrap ()Ljava/lang/String; public fun getKeyWrapBytes ()Lcom/google/protobuf/ByteString; - public fun getNonNull ()Z public fun getParserForType ()Lcom/google/protobuf/Parser; public fun getSerializedSize ()I public final fun getUnknownFields ()Lcom/google/protobuf/UnknownFieldSet; @@ -309,7 +307,6 @@ public final class com/toasttab/protokt/v1/ProtoktProtos$FieldOptions$Builder : public synthetic fun clearField (Lcom/google/protobuf/Descriptors$FieldDescriptor;)Lcom/google/protobuf/Message$Builder; public fun clearField (Lcom/google/protobuf/Descriptors$FieldDescriptor;)Lcom/toasttab/protokt/v1/ProtoktProtos$FieldOptions$Builder; public fun clearKeyWrap ()Lcom/toasttab/protokt/v1/ProtoktProtos$FieldOptions$Builder; - public fun clearNonNull ()Lcom/toasttab/protokt/v1/ProtoktProtos$FieldOptions$Builder; public synthetic fun clearOneof (Lcom/google/protobuf/Descriptors$OneofDescriptor;)Lcom/google/protobuf/AbstractMessage$Builder; public synthetic fun clearOneof (Lcom/google/protobuf/Descriptors$OneofDescriptor;)Lcom/google/protobuf/GeneratedMessageV3$Builder; public synthetic fun clearOneof (Lcom/google/protobuf/Descriptors$OneofDescriptor;)Lcom/google/protobuf/Message$Builder; @@ -333,7 +330,6 @@ public final class com/toasttab/protokt/v1/ProtoktProtos$FieldOptions$Builder : public fun getDescriptorForType ()Lcom/google/protobuf/Descriptors$Descriptor; public fun getKeyWrap ()Ljava/lang/String; public fun getKeyWrapBytes ()Lcom/google/protobuf/ByteString; - public fun getNonNull ()Z public fun getValueWrap ()Ljava/lang/String; public fun getValueWrapBytes ()Lcom/google/protobuf/ByteString; public fun getWrap ()Ljava/lang/String; @@ -360,7 +356,6 @@ public final class com/toasttab/protokt/v1/ProtoktProtos$FieldOptions$Builder : public fun setField (Lcom/google/protobuf/Descriptors$FieldDescriptor;Ljava/lang/Object;)Lcom/toasttab/protokt/v1/ProtoktProtos$FieldOptions$Builder; public fun setKeyWrap (Ljava/lang/String;)Lcom/toasttab/protokt/v1/ProtoktProtos$FieldOptions$Builder; public fun setKeyWrapBytes (Lcom/google/protobuf/ByteString;)Lcom/toasttab/protokt/v1/ProtoktProtos$FieldOptions$Builder; - public fun setNonNull (Z)Lcom/toasttab/protokt/v1/ProtoktProtos$FieldOptions$Builder; public synthetic fun setRepeatedField (Lcom/google/protobuf/Descriptors$FieldDescriptor;ILjava/lang/Object;)Lcom/google/protobuf/GeneratedMessageV3$Builder; public synthetic fun setRepeatedField (Lcom/google/protobuf/Descriptors$FieldDescriptor;ILjava/lang/Object;)Lcom/google/protobuf/Message$Builder; public fun setRepeatedField (Lcom/google/protobuf/Descriptors$FieldDescriptor;ILjava/lang/Object;)Lcom/toasttab/protokt/v1/ProtoktProtos$FieldOptions$Builder; @@ -379,7 +374,6 @@ public abstract interface class com/toasttab/protokt/v1/ProtoktProtos$FieldOptio public abstract fun getDeprecationMessageBytes ()Lcom/google/protobuf/ByteString; public abstract fun getKeyWrap ()Ljava/lang/String; public abstract fun getKeyWrapBytes ()Lcom/google/protobuf/ByteString; - public abstract fun getNonNull ()Z public abstract fun getValueWrap ()Ljava/lang/String; public abstract fun getValueWrapBytes ()Lcom/google/protobuf/ByteString; public abstract fun getWrap ()Ljava/lang/String; @@ -733,7 +727,6 @@ public abstract interface class com/toasttab/protokt/v1/ProtoktProtos$MethodOpti public final class com/toasttab/protokt/v1/ProtoktProtos$OneofOptions : com/google/protobuf/GeneratedMessageV3, com/toasttab/protokt/v1/ProtoktProtos$OneofOptionsOrBuilder { public static final field DEPRECATION_MESSAGE_FIELD_NUMBER I public static final field IMPLEMENTS_FIELD_NUMBER I - public static final field NON_NULL_FIELD_NUMBER I public fun equals (Ljava/lang/Object;)Z public static fun getDefaultInstance ()Lcom/toasttab/protokt/v1/ProtoktProtos$OneofOptions; public synthetic fun getDefaultInstanceForType ()Lcom/google/protobuf/Message; @@ -744,7 +737,6 @@ public final class com/toasttab/protokt/v1/ProtoktProtos$OneofOptions : com/goog public static final fun getDescriptor ()Lcom/google/protobuf/Descriptors$Descriptor; public fun getImplements ()Ljava/lang/String; public fun getImplementsBytes ()Lcom/google/protobuf/ByteString; - public fun getNonNull ()Z public fun getParserForType ()Lcom/google/protobuf/Parser; public fun getSerializedSize ()I public final fun getUnknownFields ()Lcom/google/protobuf/UnknownFieldSet; @@ -794,7 +786,6 @@ public final class com/toasttab/protokt/v1/ProtoktProtos$OneofOptions$Builder : public synthetic fun clearField (Lcom/google/protobuf/Descriptors$FieldDescriptor;)Lcom/google/protobuf/Message$Builder; public fun clearField (Lcom/google/protobuf/Descriptors$FieldDescriptor;)Lcom/toasttab/protokt/v1/ProtoktProtos$OneofOptions$Builder; public fun clearImplements ()Lcom/toasttab/protokt/v1/ProtoktProtos$OneofOptions$Builder; - public fun clearNonNull ()Lcom/toasttab/protokt/v1/ProtoktProtos$OneofOptions$Builder; public synthetic fun clearOneof (Lcom/google/protobuf/Descriptors$OneofDescriptor;)Lcom/google/protobuf/AbstractMessage$Builder; public synthetic fun clearOneof (Lcom/google/protobuf/Descriptors$OneofDescriptor;)Lcom/google/protobuf/GeneratedMessageV3$Builder; public synthetic fun clearOneof (Lcom/google/protobuf/Descriptors$OneofDescriptor;)Lcom/google/protobuf/Message$Builder; @@ -815,7 +806,6 @@ public final class com/toasttab/protokt/v1/ProtoktProtos$OneofOptions$Builder : public fun getDescriptorForType ()Lcom/google/protobuf/Descriptors$Descriptor; public fun getImplements ()Ljava/lang/String; public fun getImplementsBytes ()Lcom/google/protobuf/ByteString; - public fun getNonNull ()Z public final fun isInitialized ()Z public synthetic fun mergeFrom (Lcom/google/protobuf/CodedInputStream;Lcom/google/protobuf/ExtensionRegistryLite;)Lcom/google/protobuf/AbstractMessage$Builder; public synthetic fun mergeFrom (Lcom/google/protobuf/CodedInputStream;Lcom/google/protobuf/ExtensionRegistryLite;)Lcom/google/protobuf/AbstractMessageLite$Builder; @@ -837,7 +827,6 @@ public final class com/toasttab/protokt/v1/ProtoktProtos$OneofOptions$Builder : public fun setField (Lcom/google/protobuf/Descriptors$FieldDescriptor;Ljava/lang/Object;)Lcom/toasttab/protokt/v1/ProtoktProtos$OneofOptions$Builder; public fun setImplements (Ljava/lang/String;)Lcom/toasttab/protokt/v1/ProtoktProtos$OneofOptions$Builder; public fun setImplementsBytes (Lcom/google/protobuf/ByteString;)Lcom/toasttab/protokt/v1/ProtoktProtos$OneofOptions$Builder; - public fun setNonNull (Z)Lcom/toasttab/protokt/v1/ProtoktProtos$OneofOptions$Builder; public synthetic fun setRepeatedField (Lcom/google/protobuf/Descriptors$FieldDescriptor;ILjava/lang/Object;)Lcom/google/protobuf/GeneratedMessageV3$Builder; public synthetic fun setRepeatedField (Lcom/google/protobuf/Descriptors$FieldDescriptor;ILjava/lang/Object;)Lcom/google/protobuf/Message$Builder; public fun setRepeatedField (Lcom/google/protobuf/Descriptors$FieldDescriptor;ILjava/lang/Object;)Lcom/toasttab/protokt/v1/ProtoktProtos$OneofOptions$Builder; @@ -851,7 +840,6 @@ public abstract interface class com/toasttab/protokt/v1/ProtoktProtos$OneofOptio public abstract fun getDeprecationMessageBytes ()Lcom/google/protobuf/ByteString; public abstract fun getImplements ()Ljava/lang/String; public abstract fun getImplementsBytes ()Lcom/google/protobuf/ByteString; - public abstract fun getNonNull ()Z } public final class com/toasttab/protokt/v1/ProtoktProtos$ServiceOptions : com/google/protobuf/GeneratedMessageV3, com/toasttab/protokt/v1/ProtoktProtos$ServiceOptionsOrBuilder { @@ -1014,14 +1002,13 @@ public final class protokt/v1/EnumValueOptions$Deserializer : protokt/v1/Abstrac public final class protokt/v1/FieldOptions : protokt/v1/AbstractMessage { public static final field Deserializer Lprotokt/v1/FieldOptions$Deserializer; - public synthetic fun (ZLjava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lprotokt/v1/UnknownFieldSet;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lprotokt/v1/UnknownFieldSet;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun copy (Lkotlin/jvm/functions/Function1;)Lprotokt/v1/FieldOptions; public static fun deserialize (Lprotokt/v1/Reader;)Lprotokt/v1/FieldOptions; public fun equals (Ljava/lang/Object;)Z public final fun getBytesSlice ()Z public final fun getDeprecationMessage ()Ljava/lang/String; public final fun getKeyWrap ()Ljava/lang/String; - public final fun getNonNull ()Z public final fun getUnknownFields ()Lprotokt/v1/UnknownFieldSet; public final fun getValueWrap ()Ljava/lang/String; public final fun getWrap ()Ljava/lang/String; @@ -1038,14 +1025,12 @@ public final class protokt/v1/FieldOptions$Builder { public final fun getBytesSlice ()Z public final fun getDeprecationMessage ()Ljava/lang/String; public final fun getKeyWrap ()Ljava/lang/String; - public final fun getNonNull ()Z public final fun getUnknownFields ()Lprotokt/v1/UnknownFieldSet; public final fun getValueWrap ()Ljava/lang/String; public final fun getWrap ()Ljava/lang/String; public final fun setBytesSlice (Z)V public final fun setDeprecationMessage (Ljava/lang/String;)V public final fun setKeyWrap (Ljava/lang/String;)V - public final fun setNonNull (Z)V public final fun setUnknownFields (Lprotokt/v1/UnknownFieldSet;)V public final fun setValueWrap (Ljava/lang/String;)V public final fun setWrap (Ljava/lang/String;)V @@ -1155,13 +1140,12 @@ public final class protokt/v1/MethodOptions$Deserializer : protokt/v1/AbstractDe public final class protokt/v1/OneofOptions : protokt/v1/AbstractMessage { public static final field Deserializer Lprotokt/v1/OneofOptions$Deserializer; - public synthetic fun (ZLjava/lang/String;Ljava/lang/String;Lprotokt/v1/UnknownFieldSet;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Lprotokt/v1/UnknownFieldSet;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun copy (Lkotlin/jvm/functions/Function1;)Lprotokt/v1/OneofOptions; public static fun deserialize (Lprotokt/v1/Reader;)Lprotokt/v1/OneofOptions; public fun equals (Ljava/lang/Object;)Z public final fun getDeprecationMessage ()Ljava/lang/String; public final fun getImplements ()Ljava/lang/String; - public final fun getNonNull ()Z public final fun getUnknownFields ()Lprotokt/v1/UnknownFieldSet; public fun hashCode ()I public static final fun invoke (Lkotlin/jvm/functions/Function1;)Lprotokt/v1/OneofOptions; @@ -1175,11 +1159,9 @@ public final class protokt/v1/OneofOptions$Builder { public final fun build ()Lprotokt/v1/OneofOptions; public final fun getDeprecationMessage ()Ljava/lang/String; public final fun getImplements ()Ljava/lang/String; - public final fun getNonNull ()Z public final fun getUnknownFields ()Lprotokt/v1/UnknownFieldSet; public final fun setDeprecationMessage (Ljava/lang/String;)V public final fun setImplements (Ljava/lang/String;)V - public final fun setNonNull (Z)V public final fun setUnknownFields (Lprotokt/v1/UnknownFieldSet;)V } diff --git a/shared-src/reflect/protokt/v1/reflect/ClassLookup.kt b/shared-src/reflect/protokt/v1/reflect/ClassLookup.kt index 9a6d766a2..f77fb7d56 100644 --- a/shared-src/reflect/protokt/v1/reflect/ClassLookup.kt +++ b/shared-src/reflect/protokt/v1/reflect/ClassLookup.kt @@ -21,6 +21,7 @@ import protokt.v1.OptimizedSizeOfConverter import java.io.File import java.net.URLClassLoader import kotlin.reflect.KClass +import kotlin.reflect.KProperty import kotlin.reflect.full.hasAnnotation import kotlin.reflect.full.memberProperties @@ -63,11 +64,11 @@ internal class ClassLookup(classpath: List) { private val classLookup = mutableMapOf>() - fun properties(canonicalClassName: String): Collection = + fun properties(canonicalClassName: String): Collection> = try { classLookup.getOrPut(canonicalClassName) { classLoader.loadClass(canonicalClassName).kotlin - }.memberProperties.map { it.name } + }.memberProperties } catch (t: Throwable) { throw Exception("Class not found: $canonicalClassName") } diff --git a/testing/interop/src/main/proto/protokt/v1/testing/wrappers_dynamic.proto b/testing/interop/src/main/proto/protokt/v1/testing/wrappers_dynamic.proto index 278ea49f2..13bb58fad 100644 --- a/testing/interop/src/main/proto/protokt/v1/testing/wrappers_dynamic.proto +++ b/testing/interop/src/main/proto/protokt/v1/testing/wrappers_dynamic.proto @@ -75,8 +75,6 @@ message Wrappers { message OneofWrappers { oneof wrapped_oneof { - option (.protokt.v1.oneof).non_null = true; - bytes uuid_oneof = 2 [ (.protokt.v1.property).wrap = "java.util.UUID" ]; diff --git a/testing/options-api/src/main/kotlin/protokt/v1/testing/IModel2.kt b/testing/options-api/src/main/kotlin/protokt/v1/testing/IModel2.kt index 256c6cae3..bd10869b6 100644 --- a/testing/options-api/src/main/kotlin/protokt/v1/testing/IModel2.kt +++ b/testing/options-api/src/main/kotlin/protokt/v1/testing/IModel2.kt @@ -16,5 +16,5 @@ package protokt.v1.testing interface IModel2 { - val id: String + val id: String? } diff --git a/testing/options/src/main/proto/protokt/v1/testing/message_implements.proto b/testing/options/src/main/proto/protokt/v1/testing/message_implements.proto index ff568d257..ff69fe627 100644 --- a/testing/options/src/main/proto/protokt/v1/testing/message_implements.proto +++ b/testing/options/src/main/proto/protokt/v1/testing/message_implements.proto @@ -38,7 +38,5 @@ message ImplementsModel2 { message ImplementsWithDelegate { option (.protokt.v1.class).implements = "IModel2 by modelTwo"; - ImplementsModel2 model_two = 1 [ - (.protokt.v1.property).non_null = true - ]; + ImplementsModel2 model_two = 1; } diff --git a/testing/options/src/main/proto/protokt/v1/testing/non_null_property_and_oneof.proto b/testing/options/src/main/proto/protokt/v1/testing/non_null_property_and_oneof.proto deleted file mode 100644 index d348af90f..000000000 --- a/testing/options/src/main/proto/protokt/v1/testing/non_null_property_and_oneof.proto +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2019 Toast, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto3"; - -package protokt.v1.testing; - -import "google/protobuf/wrappers.proto"; -import "protokt/v1/protokt.proto"; - -message NonNullModel { - google.protobuf.StringValue non_null_string_value = 1 [ - (.protokt.v1.property).non_null = true - ]; - - oneof non_null_oneof { - option (.protokt.v1.oneof).non_null = true; - - string message = 2; - } -} - -message NonNullModelMirror { - google.protobuf.StringValue non_null_string_value = 1; - - oneof non_null_oneof { - string message = 2; - } -} diff --git a/testing/options/src/main/proto/protokt/v1/testing/oneof_implements.proto b/testing/options/src/main/proto/protokt/v1/testing/oneof_implements.proto index 96b2a21a4..6c7f48c59 100644 --- a/testing/options/src/main/proto/protokt/v1/testing/oneof_implements.proto +++ b/testing/options/src/main/proto/protokt/v1/testing/oneof_implements.proto @@ -39,7 +39,6 @@ message ImplementsOneof2 { message ContainsOneofThatImplements { oneof implementing_oneof { - option (.protokt.v1.oneof).non_null = true; option (.protokt.v1.oneof).implements = "OneofModel"; ImplementsOneof1 implements_one = 1; diff --git a/testing/options/src/main/proto/protokt/v1/testing/pkg/shared_simple_names_alternate_package.proto b/testing/options/src/main/proto/protokt/v1/testing/pkg/shared_simple_names_alternate_package.proto index 733a8ae87..7a41d5884 100644 --- a/testing/options/src/main/proto/protokt/v1/testing/pkg/shared_simple_names_alternate_package.proto +++ b/testing/options/src/main/proto/protokt/v1/testing/pkg/shared_simple_names_alternate_package.proto @@ -21,17 +21,13 @@ import "google/protobuf/duration.proto"; import "protokt/v1/protokt.proto"; message ImportsWrapperModel { - google.protobuf.Duration native_duration = 1 [ - (.protokt.v1.property).non_null = true - ]; + google.protobuf.Duration native_duration = 1; google.protobuf.Duration java_duration = 2 [ - (.protokt.v1.property).wrap = "java.time.Duration", - (.protokt.v1.property).non_null = true + (.protokt.v1.property).wrap = "java.time.Duration" ]; google.protobuf.Duration superfluous_duration = 3 [ - (.protokt.v1.property).wrap = "protokt.v1.testing.Duration", - (.protokt.v1.property).non_null = true + (.protokt.v1.property).wrap = "protokt.v1.testing.Duration" ]; } diff --git a/testing/options/src/main/proto/protokt/v1/testing/shared_simple_names.proto b/testing/options/src/main/proto/protokt/v1/testing/shared_simple_names.proto index 8c4c74b1f..79cdbb5ba 100644 --- a/testing/options/src/main/proto/protokt/v1/testing/shared_simple_names.proto +++ b/testing/options/src/main/proto/protokt/v1/testing/shared_simple_names.proto @@ -21,17 +21,13 @@ import "google/protobuf/duration.proto"; import "protokt/v1/protokt.proto"; message ImportsWrapperModel { - google.protobuf.Duration native_duration = 1 [ - (.protokt.v1.property).non_null = true - ]; + google.protobuf.Duration native_duration = 1; google.protobuf.Duration java_duration = 2 [ - (.protokt.v1.property).wrap = "java.time.Duration", - (.protokt.v1.property).non_null = true + (.protokt.v1.property).wrap = "java.time.Duration" ]; google.protobuf.Duration superfluous_duration = 3 [ - (.protokt.v1.property).wrap = "protokt.v1.testing.Duration", - (.protokt.v1.property).non_null = true + (.protokt.v1.property).wrap = "protokt.v1.testing.Duration" ]; } diff --git a/testing/options/src/main/proto/protokt/v1/testing/wrapper_types.proto b/testing/options/src/main/proto/protokt/v1/testing/wrapper_types.proto index 6f45d39ad..98aeb7f55 100644 --- a/testing/options/src/main/proto/protokt/v1/testing/wrapper_types.proto +++ b/testing/options/src/main/proto/protokt/v1/testing/wrapper_types.proto @@ -44,18 +44,15 @@ message Wrappers { ]; .protokt.v1.InetSocketAddress socket_address = 5 [ - (.protokt.v1.property).wrap = "java.net.InetSocketAddress", - (.protokt.v1.property).non_null = true + (.protokt.v1.property).wrap = "java.net.InetSocketAddress" ]; google.protobuf.Timestamp instant = 6 [ - (.protokt.v1.property).wrap = "java.time.Instant", - (.protokt.v1.property).non_null = true + (.protokt.v1.property).wrap = "java.time.Instant" ]; google.protobuf.Duration duration = 7 [ - (.protokt.v1.property).wrap = "java.time.Duration", - (.protokt.v1.property).non_null = true + (.protokt.v1.property).wrap = "java.time.Duration" ]; string local_date = 8 [ @@ -71,8 +68,7 @@ message Wrappers { ]; google.type.Date google_date = 11 [ - (.protokt.v1.property).wrap = "java.time.LocalDate", - (.protokt.v1.property).non_null = true + (.protokt.v1.property).wrap = "java.time.LocalDate" ]; optional bytes optional_uuid = 12 [ @@ -86,27 +82,10 @@ message Wrappers { optional string optional_local_date = 14 [ (.protokt.v1.property).wrap = "java.time.LocalDate" ]; - - bytes non_null_uuid = 15 [ - (.protokt.v1.property).wrap = "java.util.UUID", - (.protokt.v1.property).non_null = true - ]; - - bytes non_null_ip_address = 16 [ - (.protokt.v1.property).wrap = "java.net.InetAddress", - (.protokt.v1.property).non_null = true - ]; - - string non_null_local_date = 17 [ - (.protokt.v1.property).wrap = "java.time.LocalDate", - (.protokt.v1.property).non_null = true - ]; } message OneofWrappers { oneof wrapped_oneof { - option (.protokt.v1.oneof).non_null = true; - bytes id_oneof = 1 [ (.protokt.v1.property).wrap = "protokt.v1.testing.Id" ]; diff --git a/testing/options/src/test/kotlin/protokt/v1/testing/NonNullableTest.kt b/testing/options/src/test/kotlin/protokt/v1/testing/NonNullableTest.kt deleted file mode 100644 index 29b0dc2b5..000000000 --- a/testing/options/src/test/kotlin/protokt/v1/testing/NonNullableTest.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2019 Toast, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package protokt.v1.testing - -import com.google.common.truth.Truth.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows - -class NonNullableTest { - @Test - fun `test declared nullability`() { - assertThat( - NonNullModel::class.propertyIsMarkedNullable("nonNullStringValue") - ).isFalse() - - assertThat( - NonNullModel::class.propertyIsMarkedNullable("nonNullOneof") - ).isFalse() - } - - @Test - fun `detailed error when attempting to deserialize null field`() { - val thrown = assertThrows { - NonNullModel.deserialize( - NonNullModelMirror { - nonNullStringValue = null - nonNullOneof = NonNullModelMirror.NonNullOneof.Message("asdf") - }.serialize() - ) - } - - assertThat(thrown).hasMessageThat().apply { - contains("nonNullStringValue") - contains("was null") - contains("(protokt.property).non_null") - } - } - - @Test - fun `detailed error when attempting to deserialize null oneof`() { - val thrown = assertThrows { - NonNullModel.deserialize( - NonNullModelMirror { - nonNullStringValue = "asdf" - nonNullOneof = null - }.serialize() - ) - } - - assertThat(thrown).hasMessageThat().apply { - contains("nonNullOneof") - contains("was null") - contains("(protokt.oneof).non_null") - } - } -} diff --git a/testing/options/src/test/kotlin/protokt/v1/testing/OneofImplementsTest.kt b/testing/options/src/test/kotlin/protokt/v1/testing/OneofImplementsTest.kt index 7a5245338..3c86904b8 100644 --- a/testing/options/src/test/kotlin/protokt/v1/testing/OneofImplementsTest.kt +++ b/testing/options/src/test/kotlin/protokt/v1/testing/OneofImplementsTest.kt @@ -28,8 +28,8 @@ class OneofImplementsTest { @Test fun `property shared between oneof types can be assigned and accessed without switching`() { - val assigned: OneofModel = obj.implementingOneof + val assigned: OneofModel? = obj.implementingOneof - assertThat(assigned.id).isEqualTo(Id("val")) + assertThat(assigned?.id).isEqualTo(Id("val")) } } diff --git a/testing/options/src/test/kotlin/protokt/v1/testing/WrapperTypesTest.kt b/testing/options/src/test/kotlin/protokt/v1/testing/WrapperTypesTest.kt index 4b0abc7db..2aab57e95 100644 --- a/testing/options/src/test/kotlin/protokt/v1/testing/WrapperTypesTest.kt +++ b/testing/options/src/test/kotlin/protokt/v1/testing/WrapperTypesTest.kt @@ -17,7 +17,6 @@ package protokt.v1.testing import com.google.common.truth.Truth.assertThat import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows import protokt.v1.Bytes import protokt.v1.testing.OneofWrappers.WrappedOneof import java.net.InetAddress @@ -44,9 +43,6 @@ class WrapperTypesTest { optionalUuid = UUID.randomUUID() optionalIpAddress = InetAddress.getByAddress(byteArrayOf(0, 0, 0, 1)) optionalLocalDate = LocalDate.of(1950, 10, 4) - nonNullUuid = UUID.randomUUID() - nonNullIpAddress = InetAddress.getByAddress(byteArrayOf(0, 0, 0, 1)) - nonNullLocalDate = LocalDate.of(1950, 10, 4) } @Test @@ -64,21 +60,6 @@ class WrapperTypesTest { assertThat(Wrappers::class.propertyIsMarkedNullable("localDate")).isTrue() } - @Test - fun `nonnull uuid property is not nullable`() { - assertThat(Wrappers::class.propertyIsMarkedNullable("nonNullUuid")).isFalse() - } - - @Test - fun `nonnull ipAddress property is not nullable`() { - assertThat(Wrappers::class.propertyIsMarkedNullable("nonNullIpAddress")).isFalse() - } - - @Test - fun `nonnull localDate property is not nullable`() { - assertThat(Wrappers::class.propertyIsMarkedNullable("nonNullLocalDate")).isFalse() - } - @Test fun `round trip should preserve model`() { assertThat(Wrappers.deserialize(model.serialize())).isEqualTo(model) @@ -271,7 +252,7 @@ class WrapperTypesTest { fun `round trip should preserve socket address oneof`() { val deserialized = OneofWrappers.deserialize( OneofWrappers { - wrappedOneof = WrappedOneof.SocketAddressOneof(model.socketAddress) + wrappedOneof = WrappedOneof.SocketAddressOneof(model.socketAddress!!) }.serialize() ) @@ -284,7 +265,7 @@ class WrapperTypesTest { fun `round trip should preserve instant oneof`() { val deserialized = OneofWrappers.deserialize( OneofWrappers { - wrappedOneof = WrappedOneof.InstantOneof(model.instant) + wrappedOneof = WrappedOneof.InstantOneof(model.instant!!) }.serialize() ) @@ -297,7 +278,7 @@ class WrapperTypesTest { fun `round trip should preserve duration oneof`() { val deserialized = OneofWrappers.deserialize( OneofWrappers { - wrappedOneof = WrappedOneof.DurationOneof(model.duration) + wrappedOneof = WrappedOneof.DurationOneof(model.duration!!) }.serialize() ) @@ -323,7 +304,7 @@ class WrapperTypesTest { fun `round trip should preserve google localdate oneof`() { val deserialized = OneofWrappers.deserialize( OneofWrappers { - wrappedOneof = WrappedOneof.GoogleDateOneof(model.googleDate) + wrappedOneof = WrappedOneof.GoogleDateOneof(model.googleDate!!) }.serialize() ) @@ -332,16 +313,6 @@ class WrapperTypesTest { ).isEqualTo(model.googleDate) } - @Test - fun `wrapped message should not be nullable`() { - val thrown = assertThrows { - model.copy { instant = null } - } - - assertThat(thrown).hasMessageThat() - .isEqualTo("instant specified nonnull with (protokt.property).non_null but was null") - } - @Test fun `round trip preserves repeated wrapped types`() { val list = listOf(UUID.randomUUID()) diff --git a/testing/options/src/test/kotlin/protokt/v1/testing/pkg/SharedSimpleNamesAlternativePackageTest.kt b/testing/options/src/test/kotlin/protokt/v1/testing/pkg/SharedSimpleNamesAlternativePackageTest.kt index c1e13a4b0..fd4e223e9 100644 --- a/testing/options/src/test/kotlin/protokt/v1/testing/pkg/SharedSimpleNamesAlternativePackageTest.kt +++ b/testing/options/src/test/kotlin/protokt/v1/testing/pkg/SharedSimpleNamesAlternativePackageTest.kt @@ -30,11 +30,11 @@ class SharedSimpleNamesAlternativePackageTest { fun checkDurationTypes(klass: KClass<*>) { assertThat(klass.propertyType("nativeDuration")) - .isEqualTo(protokt.v1.google.protobuf.Duration::class.createType()) + .isEqualTo(protokt.v1.google.protobuf.Duration::class.createType(nullable = true)) assertThat(klass.propertyType("javaDuration")) - .isEqualTo(java.time.Duration::class.createType()) + .isEqualTo(java.time.Duration::class.createType(nullable = true)) assertThat(klass.propertyType("superfluousDuration")) - .isEqualTo(protokt.v1.testing.Duration::class.createType()) + .isEqualTo(protokt.v1.testing.Duration::class.createType(nullable = true)) }