From 527ac508f58910ee5764f26599b78b038b1d8874 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Mon, 28 Apr 2025 17:01:37 +0200 Subject: [PATCH 1/2] Mention type unsoundness of `serializer()` in the documentation Fixes #2948 --- .../src/kotlinx/serialization/Serializers.kt | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/core/commonMain/src/kotlinx/serialization/Serializers.kt b/core/commonMain/src/kotlinx/serialization/Serializers.kt index 4e44d3d2b2..e47ceaf3f7 100644 --- a/core/commonMain/src/kotlinx/serialization/Serializers.kt +++ b/core/commonMain/src/kotlinx/serialization/Serializers.kt @@ -58,14 +58,14 @@ public inline fun SerializersModule.serializer(): KSerializer { * Creates a serializer for the given [type]. * [type] argument is usually obtained with [typeOf] method. * - * This overload works with full type information, including type arguments and nullability, - * and is a recommended way to retrieve a serializer. - * For example, `serializer>>()` returns [KSerializer] that is able - * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`. - * * Variance of [type]'s type arguments is not used by the serialization and is not taken into account. * Star projections in [type]'s arguments are prohibited. * + * **Pitfall**: the returned serializer may return incorrect results or throw a [ClassCastException] if it receives + * a value that's not a valid instance of the [KType], even though the type allows passing such a value. + * Consider using the `serializer()` overload accepting a type argument (for example, `serializer>()`), + * which returns the serializer with the correct type. + * * @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable). * @throws IllegalArgumentException if any of [type]'s arguments contains star projection */ @@ -80,11 +80,16 @@ public fun serializer(type: KType): KSerializer = EmptySerializersModule() * The nullability of returned serializer is specified using the [isNullable]. * * Note that it is impossible to create an array serializer with this method, - * as array serializer needs additional information: type token for an element type. + * as an array serializer needs additional information: type token for an element type. * To create array serializer, use overload with [KType] or [ArraySerializer] directly. * * Caching on JVM platform is disabled for this function, so it may work slower than an overload with [KType]. * + * **Pitfall**: the returned serializer may return incorrect results or throw a [ClassCastException] if it receives + * a value that's not a valid instance of the [KType], even though the type allows passing such a value. + * Consider using the `serializer()` overload accepting a type argument (for example, `serializer>()`), + * which returns the serializer with the correct type. + * * @throws SerializationException if serializer cannot be created (provided [kClass] or its type argument is not serializable) * @throws SerializationException if [kClass] is a `kotlin.Array` * @throws SerializationException if size of [typeArgumentsSerializers] does not match the expected generic parameters count @@ -100,14 +105,12 @@ public fun serializer( * Creates a serializer for the given [type] if possible. * [type] argument is usually obtained with [typeOf] method. * - * This overload works with full type information, including type arguments and nullability, - * and is a recommended way to retrieve a serializer. - * For example, `serializerOrNull>>()` returns [KSerializer] that is able - * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`. - * * Variance of [type]'s arguments is not used by the serialization and is not taken into account. * Star projections in [type]'s arguments are prohibited. * + * **Pitfall**: the returned serializer may return incorrect results or throw a [ClassCastException] if it receives + * a value that's not a valid instance of the [KType], even though the type allows passing such a value. + * * @return [KSerializer] for the given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable). * @throws IllegalArgumentException if any of [type]'s arguments contains star projection */ @@ -121,11 +124,17 @@ public fun serializerOrNull(type: KType): KSerializer? = EmptySerializersM * This overload works with full type information, including type arguments and nullability, * and is a recommended way to retrieve a serializer. * For example, `serializer>>()` returns [KSerializer] that is able - * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`. + * to serialize and deserialize a list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`. * * Variance of [type]'s arguments is not used by the serialization and is not taken into account. * Star projections in [type]'s arguments are prohibited. * + * **Pitfall**: the returned serializer may return incorrect results or throw a [ClassCastException] if it receives + * a value that's not a valid instance of the [KType], even though the type allows passing such a value. + * Consider using the `serializer()` overload accepting a type argument + * (for example, `module.serializer>()`), + * which returns the serializer with the correct type. + * * @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable and is not registered in [this] module). * @throws IllegalArgumentException if any of [type]'s arguments contains star projection */ @@ -143,11 +152,17 @@ public fun SerializersModule.serializer(type: KType): KSerializer = * The nullability of returned serializer is specified using the [isNullable]. * * Note that it is impossible to create an array serializer with this method, - * as array serializer needs additional information: type token for an element type. + * as an array serializer needs additional information: type token for an element type. * To create array serializer, use overload with [KType] or [ArraySerializer] directly. * * Caching on JVM platform is disabled for this function, so it may work slower than an overload with [KType]. * + * **Pitfall**: the returned serializer may return incorrect results or throw a [ClassCastException] if it receives + * a value that's not a valid instance of the [KType], even though the type allows passing such a value. + * Consider using the `serializer()` overload accepting a type argument + * (for example, `module.serializer>()`), + * which returns the serializer with the correct type. + * * @throws SerializationException if serializer cannot be created (provided [kClass] or its type argument is not serializable and is not registered in [this] module) * @throws SerializationException if [kClass] is a `kotlin.Array` * @throws SerializationException if size of [typeArgumentsSerializers] does not match the expected generic parameters count @@ -174,6 +189,9 @@ public fun SerializersModule.serializer( * Variance of [type]'s arguments is not used by the serialization and is not taken into account. * Star projections in [type]'s arguments are prohibited. * + * **Pitfall**: the returned serializer may return incorrect results or throw a [ClassCastException] if it receives + * a value that's not a valid instance of the [KType], even though the type allows passing such a value. + * * @return [KSerializer] for the given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable and is not registered in [this] module). * @throws IllegalArgumentException if any of [type]'s arguments contains star projection */ From 82a33214568b9ad786995c79b1379881ffbc7af1 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Thu, 15 May 2025 13:58:53 +0200 Subject: [PATCH 2/2] Return the removed paragraphs, satisfy the spell checker --- .../src/kotlinx/serialization/Serializers.kt | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/core/commonMain/src/kotlinx/serialization/Serializers.kt b/core/commonMain/src/kotlinx/serialization/Serializers.kt index e47ceaf3f7..da09fb17db 100644 --- a/core/commonMain/src/kotlinx/serialization/Serializers.kt +++ b/core/commonMain/src/kotlinx/serialization/Serializers.kt @@ -23,7 +23,7 @@ import kotlin.reflect.* * This overload works with full type information, including type arguments and nullability, * and is a recommended way to retrieve a serializer. * For example, `serializer>()` returns [KSerializer] that is able - * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`. + * to serialize and deserialize a list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`. * * Variance of [T]'s type arguments is not used by the serialization and is not taken into account. * Star projections in [T]'s type arguments are prohibited. @@ -42,7 +42,7 @@ public inline fun serializer(): KSerializer { * This overload works with full type information, including type arguments and nullability, * and is a recommended way to retrieve a serializer. * For example, `serializer>()` returns [KSerializer] that is able - * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`. + * to serialize and deserialize a list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`. * * Variance of [T]'s type arguments is not used by the serialization and is not taken into account. * Star projections in [T]'s type arguments are prohibited. @@ -58,6 +58,11 @@ public inline fun SerializersModule.serializer(): KSerializer { * Creates a serializer for the given [type]. * [type] argument is usually obtained with [typeOf] method. * + * This overload works with full type information, including type arguments and nullability, + * and is a recommended way to retrieve a serializer. + * For example, `serializerOrNull(typeOf>)` returns [KSerializer] that is able + * to serialize and deserialize a list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`. + * * Variance of [type]'s type arguments is not used by the serialization and is not taken into account. * Star projections in [type]'s arguments are prohibited. * @@ -105,6 +110,11 @@ public fun serializer( * Creates a serializer for the given [type] if possible. * [type] argument is usually obtained with [typeOf] method. * + * This overload works with full type information, including type arguments and nullability, + * and is a recommended way to retrieve a serializer. + * For example, `serializerOrNull>>()` returns [KSerializer] that is able + * to serialize and deserialize a list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`. + * * Variance of [type]'s arguments is not used by the serialization and is not taken into account. * Star projections in [type]'s arguments are prohibited. * @@ -123,7 +133,7 @@ public fun serializerOrNull(type: KType): KSerializer? = EmptySerializersM * * This overload works with full type information, including type arguments and nullability, * and is a recommended way to retrieve a serializer. - * For example, `serializer>>()` returns [KSerializer] that is able + * For example, `serializer(typeOf>)` returns [KSerializer] that is able * to serialize and deserialize a list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`. * * Variance of [type]'s arguments is not used by the serialization and is not taken into account. @@ -158,7 +168,7 @@ public fun SerializersModule.serializer(type: KType): KSerializer = * Caching on JVM platform is disabled for this function, so it may work slower than an overload with [KType]. * * **Pitfall**: the returned serializer may return incorrect results or throw a [ClassCastException] if it receives - * a value that's not a valid instance of the [KType], even though the type allows passing such a value. + * a value that's not a valid instance of the [KClass], even though the type allows passing such a value. * Consider using the `serializer()` overload accepting a type argument * (for example, `module.serializer>()`), * which returns the serializer with the correct type. @@ -184,7 +194,7 @@ public fun SerializersModule.serializer( * This overload works with full type information, including type arguments and nullability, * and is a recommended way to retrieve a serializer. * For example, `serializerOrNull>>()` returns [KSerializer] that is able - * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`. + * to serialize and deserialize a list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`. * * Variance of [type]'s arguments is not used by the serialization and is not taken into account. * Star projections in [type]'s arguments are prohibited. @@ -294,24 +304,24 @@ internal fun SerializersModule.serializersForParameters( * The given class must be annotated with [Serializable] or be one of the built-in types. * * This method uses platform-specific reflection available for the given erased `KClass` - * and is not recommended to use this method for anything, but last-ditch resort, e.g. - * when all type info is lost, your application has crashed and it is the final attempt to log or send some serializable data. + * and is not recommended to use this method for anything, but last-ditch resort, e.g., + * when all type info is lost, your application has crashed, and it is the final attempt to log or send some serializable data. * * The recommended way to retrieve the serializer is inline [serializer] function and [`serializer(KType)`][serializer] * * This API is not guaranteed to work consistently across different platforms or - * to work in cases that slightly differ from "plain @Serializable class" and have platform and reflection specific limitations. + * to work in cases that slightly differ from "plain @Serializable class" and have platform- and reflection-specific limitations. * * ### Constraints * This paragraph explains known (but not all!) constraints of the `serializer()` implementation. - * Please note that they are not bugs, but implementation restrictions that we cannot workaround. + * Please note that they are not bugs but implementation restrictions that we cannot work around. * * * This method may behave differently on JVM, JS and Native because of runtime reflection differences * * Serializers for classes with generic parameters are ignored by this method - * * External serializers generated with `Serializer(forClass = )` are not lookuped consistently - * * Serializers for classes with named companion objects are not lookuped consistently + * * External serializers generated with `Serializer(forClass = )` are not looked up consistently + * * Serializers for classes with named companion objects are not looked up consistently * - * @throws SerializationException if serializer can't be found. + * @throws SerializationException if the serializer can't be found. */ @InternalSerializationApi public fun KClass.serializer(): KSerializer = serializerOrNull() ?: serializerNotRegistered() @@ -320,20 +330,20 @@ public fun KClass.serializer(): KSerializer = serializerOrNull() * Retrieves a [KSerializer] for the given [KClass] or returns `null` if none is found. * The given class must be annotated with [Serializable] or be one of the built-in types. * This method uses platform-specific reflection available for the given erased `KClass` - * and it is not recommended to use this method for anything, but last-ditch resort, e.g. - * when all type info is lost, your application has crashed and it is the final attempt to log or send some serializable data. + * and it is not recommended to use this method for anything, but last-ditch resort, e.g., + * when all type info is lost, your application has crashed, and it is the final attempt to log or send some serializable data. * * This API is not guaranteed to work consistently across different platforms or * to work in cases that slightly differ from "plain @Serializable class". * * ### Constraints * This paragraph explains known (but not all!) constraints of the `serializerOrNull()` implementation. - * Please note that they are not bugs, but implementation restrictions that we cannot workaround. + * Please note that they are not bugs but implementation restrictions that we cannot work around. * * * This method may behave differently on JVM, JS and Native because of runtime reflection differences * * Serializers for classes with generic parameters are ignored by this method - * * External serializers generated with `Serializer(forClass = )` are not lookuped consistently - * * Serializers for classes with named companion objects are not lookuped consistently + * * External serializers generated with `Serializer(forClass = )` are not looked up consistently + * * Serializers for classes with named companion objects are not looked up consistently */ @InternalSerializationApi public fun KClass.serializerOrNull(): KSerializer? =