diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeDeclarationGenerator.kt b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeDeclarationGenerator.kt index f27a59b2bae..4a4e1c28efb 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeDeclarationGenerator.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeDeclarationGenerator.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.parcelize.ParcelizeNames.OLD_PARCELIZE_FQN import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELIZE_FQN import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCEL_ID import org.jetbrains.kotlin.parcelize.ParcelizeNames.WRITE_TO_PARCEL_NAME +import org.jetbrains.kotlin.parcelize.fir.diagnostics.checkParcelizeClassSymbols import org.jetbrains.kotlin.utils.addToStdlib.runIf class FirParcelizeDeclarationGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) { @@ -106,18 +107,8 @@ class FirParcelizeDeclarationGenerator(session: FirSession) : FirDeclarationGene override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>, context: MemberGenerationContext): Set { return when { classSymbol.rawStatus.modality == Modality.ABSTRACT || classSymbol.rawStatus.modality == Modality.SEALED -> emptySet() - classSymbol in matchedClasses && classSymbol.rawStatus.modality != Modality.SEALED -> parcelizeMethodsNames - else -> { - val hasAnnotatedSealedSuperType = classSymbol.resolvedSuperTypeRefs.any { - val superSymbol = it.type.fullyExpandedType(session).toRegularClassSymbol(session) ?: return@any false - superSymbol.rawStatus.modality == Modality.SEALED && superSymbol in matchedClasses - } - if (hasAnnotatedSealedSuperType) { - parcelizeMethodsNames - } else { - emptySet() - } - } + checkParcelizeClassSymbols(classSymbol, session) { it in matchedClasses } -> parcelizeMethodsNames + else -> emptySet() } } diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeClassChecker.kt b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeClassChecker.kt index 5c6c56ec258..6e01dda44a2 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeClassChecker.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeClassChecker.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.parcelize.fir.diagnostics import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies import org.jetbrains.kotlin.diagnostics.reportOn @@ -14,7 +15,6 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.* -import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.types.* @@ -116,13 +116,30 @@ fun FirClassSymbol<*>?.isParcelize(session: FirSession): Boolean { } if (this == null) return false - if (this.annotations.any { it.toAnnotationClassId(session) in PARCELIZE_CLASS_CLASS_IDS }) return true - return resolvedSuperTypeRefs.any { superTypeRef -> - val symbol = superTypeRef.type.fullyExpandedType(session).toRegularClassSymbol(session) ?: return@any false + return checkParcelizeClassSymbols(this, session) { symbol -> symbol.annotations.any { it.toAnnotationClassId(session) in PARCELIZE_CLASS_CLASS_IDS } } } +/** + * Check all related [FirClassSymbol]s to the provided [symbol] which are valid locations for a + * `Parcelize` annotation to be present. This commonizes class symbol navigation between checker and + * generator, even though [predicate] implementation is different. + */ +inline fun checkParcelizeClassSymbols( + symbol: FirClassSymbol<*>, + session: FirSession, + predicate: (FirClassSymbol<*>) -> Boolean, +): Boolean { + if (predicate(symbol)) return true + return symbol.resolvedSuperTypeRefs.any { superTypeRef -> + val superTypeSymbol = superTypeRef.type.toRegularClassSymbol(session) + ?.takeIf { it.rawStatus.modality == Modality.SEALED } + ?: return@any false + predicate(superTypeSymbol) + } +} + fun FirRegularClass.hasCustomParceler(session: FirSession): Boolean { val companion = companionObjectSymbol ?: return false return lookupSuperTypes(companion, lookupInterfaces = true, deep = true, useSiteSession = session).any { diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/modality.fir.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/modality.fir.kt index 816c7f96d06..acd82f96c04 100644 --- a/plugins/parcelize/parcelize-compiler/testData/diagnostics/modality.fir.kt +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/modality.fir.kt @@ -6,6 +6,8 @@ import android.os.Parcelable @Parcelize open class Open(val foo: String) : Parcelable +data class Derived(val bar: String) : Open(bar) + @Parcelize class Final(val foo: String) : Parcelable @@ -29,6 +31,8 @@ fun foo() { @Parcelize object : Parcelable {} + object : Open("") {} + @Parcelize class Local {} } diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/modality.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/modality.kt index 479b51a91fa..358eca03f2f 100644 --- a/plugins/parcelize/parcelize-compiler/testData/diagnostics/modality.kt +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/modality.kt @@ -6,6 +6,8 @@ import android.os.Parcelable @Parcelize open class Open(val foo: String) : Parcelable +data class Derived(val bar: String) : Open(bar) + @Parcelize class Final(val foo: String) : Parcelable @@ -29,6 +31,8 @@ fun foo() { @Parcelize object : Parcelable {} + object : Open("") {} + @Parcelize class Local {} }