Prohibit Array<T> in @Serializable classes
because it's impossible to correctly create array reflectively without knowing correct KClass. Fixes https://github.com/Kotlin/kotlinx.serialization/issues/1243
This commit is contained in:
committed by
Space Team
parent
596949a501
commit
090aec6b3b
+2
@@ -32,6 +32,8 @@ public interface SerializationErrors {
|
||||
DiagnosticFactory1<PsiElement, KotlinType> LOCAL_SERIALIZER_USAGE = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> TRANSIENT_MISSING_INITIALIZER = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<PsiElement> GENERIC_ARRAY_ELEMENT_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<PsiElement> TRANSIENT_IS_REDUNDANT = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> JSON_FORMAT_REDUNDANT_DEFAULT = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> JSON_FORMAT_REDUNDANT = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
+16
-1
@@ -69,7 +69,8 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
val entry = classDescriptor.findAnnotationDeclaration(SerializationAnnotations.serializerAnnotationFqName)
|
||||
val inSameModule =
|
||||
trace.bindingContext[BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, serializableDescriptor.fqNameUnsafe] != null
|
||||
val diagnostic = if (inSameModule) SerializationErrors.EXTERNAL_CLASS_NOT_SERIALIZABLE else SerializationErrors.EXTERNAL_CLASS_IN_ANOTHER_MODULE
|
||||
val diagnostic =
|
||||
if (inSameModule) SerializationErrors.EXTERNAL_CLASS_NOT_SERIALIZABLE else SerializationErrors.EXTERNAL_CLASS_IN_ANOTHER_MODULE
|
||||
|
||||
trace.report(diagnostic.on(entry ?: declaration, classDescriptor.defaultType, serializableKType))
|
||||
}
|
||||
@@ -263,6 +264,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.isSerializableEnumWithMissingSerializer(): Boolean {
|
||||
if (kind != ClassKind.ENUM_CLASS) return false
|
||||
if (hasSerializableOrMetaAnnotation) return false
|
||||
@@ -344,10 +346,23 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
generatorContextForAnalysis.checkTypeArguments(it.module, it.type, element, trace, propertyPsi)
|
||||
} else {
|
||||
generatorContextForAnalysis.checkType(it.module, it.type, ktType, trace, propertyPsi)
|
||||
checkGenericArrayType(it.type, ktType, trace, propertyPsi)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkGenericArrayType(
|
||||
type: KotlinType,
|
||||
ktType: KtTypeReference?,
|
||||
trace: BindingTrace,
|
||||
fallbackElement: PsiElement
|
||||
) {
|
||||
if (KotlinBuiltIns.isArray(type) && type.arguments.first().type.genericIndex != null) {
|
||||
// Array<T> is unsupported, since we can't get T::class from KSerializer<T>
|
||||
trace.report(SerializationErrors.GENERIC_ARRAY_ELEMENT_NOT_SUPPORTED.on(ktType ?: fallbackElement))
|
||||
}
|
||||
}
|
||||
|
||||
private fun AbstractSerialGenerator.checkTypeArguments(
|
||||
module: ModuleDescriptor,
|
||||
type: KotlinType,
|
||||
|
||||
+4
@@ -95,6 +95,10 @@ object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension {
|
||||
SerializationErrors.TRANSIENT_IS_REDUNDANT,
|
||||
"Property does not have backing field which makes it non-serializable and therefore @Transient is redundant"
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.GENERIC_ARRAY_ELEMENT_NOT_SUPPORTED,
|
||||
"Serialization of Arrays with generic type arguments is impossible because of unknown compile-time type."
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.JSON_FORMAT_REDUNDANT_DEFAULT,
|
||||
"Redundant creation of Json default format. Creating instances for each usage can be slow."
|
||||
|
||||
+1
-1
@@ -29,10 +29,10 @@ object FirSerializationErrors {
|
||||
val SERIALIZER_NULLABILITY_INCOMPATIBLE by error2<PsiElement, ConeKotlinType, ConeKotlinType>()
|
||||
val SERIALIZER_TYPE_INCOMPATIBLE by warning3<PsiElement, ConeKotlinType, ConeKotlinType, ConeKotlinType>()
|
||||
val LOCAL_SERIALIZER_USAGE by error1<PsiElement, ConeKotlinType>()
|
||||
val GENERIC_ARRAY_ELEMENT_NOT_SUPPORTED by error0<PsiElement>()
|
||||
val TRANSIENT_MISSING_INITIALIZER by error0<PsiElement>()
|
||||
|
||||
val TRANSIENT_IS_REDUNDANT by warning0<PsiElement>()
|
||||
|
||||
val INCORRECT_TRANSIENT by warning0<PsiElement>()
|
||||
|
||||
val REQUIRED_KOTLIN_TOO_HIGH by error3<KtAnnotationEntry, String, String, String>()
|
||||
|
||||
+13
-2
@@ -204,9 +204,9 @@ object FirSerializationPluginClassChecker : FirClassChecker() {
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
if (!classSymbol.hasSerializableOrMetaAnnotation) return false
|
||||
|
||||
|
||||
if (classSymbol.isAnonymousObjectOrInsideIt) {
|
||||
reporter.reportOn(classSymbol.serializableOrMetaAnnotationSource, FirSerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED)
|
||||
return false
|
||||
@@ -370,10 +370,21 @@ object FirSerializationPluginClassChecker : FirClassChecker() {
|
||||
checkSerializerNullability(propertyType, serializerType, source, reporter)
|
||||
} else {
|
||||
checkType(propertyType, source, reporter)
|
||||
checkGenericArrayType(propertyType, source, reporter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
private fun checkGenericArrayType(propertyType: ConeKotlinType, source: KtSourceElement?, reporter: DiagnosticReporter) {
|
||||
if (propertyType.isNonPrimitiveArray && propertyType.typeArguments.first().type?.isTypeParameter == true) {
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirSerializationErrors.GENERIC_ARRAY_ELEMENT_NOT_SUPPORTED,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun checkTypeArguments(type: ConeKotlinType, source: KtSourceElement?, reporter: DiagnosticReporter) {
|
||||
|
||||
+4
@@ -102,6 +102,10 @@ object KtDefaultErrorMessagesSerialization {
|
||||
FirSerializationErrors.INCORRECT_TRANSIENT,
|
||||
"@kotlin.jvm.Transient does not affect @Serializable classes. Please use @kotlinx.serialization.Transient instead."
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.GENERIC_ARRAY_ELEMENT_NOT_SUPPORTED,
|
||||
"Serialization of Arrays with generic type arguments is impossible because of unknown compile-time type."
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.REQUIRED_KOTLIN_TOO_HIGH,
|
||||
"Your current Kotlin version is {0}, while kotlinx.serialization core runtime {1} requires at least Kotlin {2}. " +
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// SKIP_TXT
|
||||
import kotlinx.serialization.*
|
||||
|
||||
@Serializable
|
||||
class C(val values: IntArray) // OK
|
||||
|
||||
@Serializable
|
||||
class B(val values: Array<String>) // OK
|
||||
|
||||
@Serializable
|
||||
class A<T>(val values: <!GENERIC_ARRAY_ELEMENT_NOT_SUPPORTED!>Array<T><!>)
|
||||
+6
@@ -44,6 +44,12 @@ public class SerializationFirDiagnosticTestGenerated extends AbstractSerializati
|
||||
runTest("plugins/kotlinx-serialization/testData/diagnostics/ExternalSerializers.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("GenericArrays.kt")
|
||||
public void testGenericArrays() throws Exception {
|
||||
runTest("plugins/kotlinx-serialization/testData/diagnostics/GenericArrays.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("IncorrectTransient.kt")
|
||||
public void testIncorrectTransient() throws Exception {
|
||||
|
||||
+6
@@ -42,6 +42,12 @@ public class SerializationPluginDiagnosticTestGenerated extends AbstractSerializ
|
||||
runTest("plugins/kotlinx-serialization/testData/diagnostics/ExternalSerializers.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("GenericArrays.kt")
|
||||
public void testGenericArrays() throws Exception {
|
||||
runTest("plugins/kotlinx-serialization/testData/diagnostics/GenericArrays.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("IncorrectTransient.kt")
|
||||
public void testIncorrectTransient() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user