From 0d36462c58925f08a2fd68927056cfe482d3c381 Mon Sep 17 00:00:00 2001 From: Brian Norman Date: Tue, 7 Nov 2023 13:59:17 -0600 Subject: [PATCH] [Parcelize] Align checker and generator on valid Parcelize classes A previous fix to the generator made the generator and checker validation of classes which could be parcelized different. The checker would report error in cases where the generator would not generate anything. Align these checks, with improved code sharing, so errors are not reported on classes which will not have parcelize components generated. ^KT-63086 Fixed --- .../fir/FirParcelizeDeclarationGenerator.kt | 15 +++-------- .../diagnostics/FirParcelizeClassChecker.kt | 25 ++++++++++++++++--- .../testData/diagnostics/modality.fir.kt | 4 +++ .../testData/diagnostics/modality.kt | 4 +++ 4 files changed, 32 insertions(+), 16 deletions(-) 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 {} }