diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt index a90be6f0019..fb4866ee86d 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt @@ -317,3 +317,15 @@ fun FirRegularClassSymbol.getSuperClassSymbolOrAny(session: FirSession): FirRegu } return session.builtinTypes.anyType.type.toRegularClassSymbol(session) ?: error("Symbol for Any not found") } + +fun FirClassLikeSymbol<*>.getSuperTypes( + useSiteSession: FirSession, + recursive: Boolean = true, + lookupInterfaces: Boolean = true, + substituteSuperTypes: Boolean = true, + supertypeSupplier: SupertypeSupplier = SupertypeSupplier.Default, +): List { + return SmartList().also { + collectSuperTypes(it, SmartSet.create(), recursive, lookupInterfaces, substituteSuperTypes, useSiteSession, supertypeSupplier) + } +} diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt index b44af574644..6b95dcb515f 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt @@ -18,9 +18,9 @@ import org.jetbrains.kotlin.fir.declarations.toAnnotationClassLikeType import org.jetbrains.kotlin.fir.declarations.utils.fromPrimaryConstructor import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.resolve.getSuperTypes import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.parcelize.ParcelizeNames import org.jetbrains.kotlin.parcelize.ParcelizeNames.DEPRECATED_RUNTIME_PACKAGE @@ -106,26 +106,27 @@ object FirParcelizeAnnotationChecker : FirAnnotationCallChecker() { // For `@WriteWith

` check that `P` is an object. val parcelerType = annotationCall.typeArguments.singleOrNull()?.toConeTypeProjection()?.type ?: return - if (parcelerType.toRegularClassSymbol(context.session)?.classKind != ClassKind.OBJECT) { + val parcelerTypeSymbol = parcelerType.toRegularClassSymbol(context.session) + if (parcelerTypeSymbol?.classKind != ClassKind.OBJECT) { val reportElement = annotationCall.typeArguments.singleOrNull()?.source ?: annotationCall.source reporter.reportOn(reportElement, KtErrorsParcelize.PARCELER_SHOULD_BE_OBJECT, context) } - // For `@WriteWith

T` check that `P` is a subtype of `Parceler`. + // For `@WriteWith

T` where `P` is a subtype of `Parceler`, check that T is a subtype of E. // // From the perspective of the `WriteWith` annotation call, `T` corresponds to the nearest enclosing annotation container // stripped of annotations. // // It's safe to assume that `Parceler` refers to `kotlinx.parcelize.Parceler` rather than `kotlinx.android.parcel.Parceler`, // since using the deprecated `WriteWith` annotation is an error. - val targetType = (context.annotationContainers.lastOrNull() as? FirTypeRef)?.coneType?.withAttributes(ConeAttributes.Empty) - ?: return - val expectedType = ConeClassLikeTypeImpl( - ParcelizeNames.PARCELER_ID.toLookupTag(), - arrayOf(targetType), - isNullable = false - ) - if (!parcelerType.isSubtypeOf(expectedType, context.session)) { + val targetType = (context.annotationContainers.lastOrNull() as? FirTypeRef)?.coneType + ?.withAttributes(ConeAttributes.Empty) ?: return + val parcelerSuperType = parcelerTypeSymbol?.getSuperTypes(context.session) + ?.firstOrNull { it.classId == ParcelizeNames.PARCELER_ID } ?: return + val expectedType = parcelerSuperType.typeArguments.singleOrNull()?.type + ?.withAttributes(ConeAttributes.Empty) ?: return + + if (!targetType.isSubtypeOf(expectedType, context.session)) { val reportElement = annotationCall.typeArguments.singleOrNull()?.source ?: annotationCall.source reporter.reportOn(reportElement, KtErrorsParcelize.PARCELER_TYPE_INCOMPATIBLE, parcelerType, targetType, context) } diff --git a/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt index 421cda48b0e..b96bcc143a3 100644 --- a/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt +++ b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt @@ -30,10 +30,10 @@ object Parceler3 : Parceler { @Parcelize @TypeParceler data class Test( - val a: String, - @TypeParceler val b: String, - @TypeParceler val c: CharSequence, - val d: @WriteWith String + val a: String, + @TypeParceler val b: String, +@TypeParceler val c: CharSequence, +val d: @WriteWith String ) : Parcelable fun box() = parcelTest { parcel -> diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/customParcelers.fir.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/customParcelers.fir.kt deleted file mode 100644 index b1a7f813540..00000000000 --- a/plugins/parcelize/parcelize-compiler/testData/diagnostics/customParcelers.fir.kt +++ /dev/null @@ -1,78 +0,0 @@ -// WITH_STDLIB -package test - -import kotlinx.parcelize.* -import android.os.* - -class Data -typealias StringData = Data -typealias NullableStringData = Data? - -object StringParceler : Parceler { - override fun create(parcel: Parcel) = TODO() - override fun String.write(parcel: Parcel, flags: Int) = TODO() -} - -object CharSequenceParceler : Parceler { - override fun create(parcel: Parcel) = TODO() - override fun CharSequence.write(parcel: Parcel, flags: Int) = TODO() -} - -class StringClassParceler : Parceler { - override fun create(parcel: Parcel) = TODO() - override fun String.write(parcel: Parcel, flags: Int) = TODO() -} - -class StringDataParceler : Parceler { - override fun create(parcel: Parcel) = TODO() - override fun StringData.write(parcel: Parcel, flags: Int) = TODO() -} - -class NullableStringDataParceler : Parceler { - override fun create(parcel: Parcel) = TODO() - override fun NullableStringData.write(parcel: Parcel, flags: Int) = TODO() -} - -@TypeParceler -class MissingParcelizeAnnotation(val a: @WriteWith String) - -@Parcelize -@TypeParceler -class ShouldBeClass(val a: @WriteWith<StringClassParceler> String) : Parcelable - -@Parcelize -class Test( - val a: @WriteWith<StringParceler> Int, - val b: @WriteWith String, - val c: @WriteWith<StringParceler> CharSequence, - val d: @WriteWith<CharSequenceParceler> String, - val e: @WriteWith CharSequence -) : Parcelable - -@Parcelize -@TypeParceler -class Test2(@TypeParceler val a: String) : Parcelable - -@Parcelize -@TypeParceler<String, StringParceler> -@TypeParceler<String, CharSequenceParceler> -class Test3(val a: String) : Parcelable - -@Parcelize -@TypeParceler -class StringDataParcelerTest( - val a: StringData, - val b: StringData?, - val c: Data, - val d: Data?, -) : Parcelable - -@Parcelize -@TypeParceler -class NullableStringDataParcelerTest( - val a: NullableStringData, - val b: StringData, - val c: StringData?, - val d: Data, - val e: Data?, -) : Parcelable diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/customParcelers.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/customParcelers.kt index 1ec1a2ce8d7..a51007f88ac 100644 --- a/plugins/parcelize/parcelize-compiler/testData/diagnostics/customParcelers.kt +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/customParcelers.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB package test @@ -18,6 +19,8 @@ object CharSequenceParceler : Parceler { override fun CharSequence.write(parcel: Parcel, flags: Int) = TODO() } +typealias CharSequenceParcelerAlias = CharSequenceParceler + class StringClassParceler : Parceler { override fun create(parcel: Parcel) = TODO() override fun String.write(parcel: Parcel, flags: Int) = TODO() @@ -46,7 +49,9 @@ class Test( val b: @WriteWith String, val c: @WriteWith<StringParceler> CharSequence, val d: @WriteWith String, - val e: @WriteWith CharSequence + val e: @WriteWith CharSequence, + val f: @WriteWith String, + val g: @WriteWith CharSequence, ) : Parcelable @Parcelize