[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
This commit is contained in:
Brian Norman
2023-11-07 13:59:17 -06:00
committed by Space Team
parent d5fe86559d
commit 0d36462c58
4 changed files with 32 additions and 16 deletions
@@ -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<Name> {
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()
}
}
@@ -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 {
@@ -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
<!ABSTRACT_MEMBER_NOT_IMPLEMENTED, PARCELABLE_SHOULD_BE_CLASS!>object<!> : Parcelable {}
object : Open("") {}
@Parcelize
class <!NO_PARCELABLE_SUPERTYPE, PARCELABLE_CANT_BE_LOCAL_CLASS!>Local<!> {}
}
@@ -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
<!PARCELABLE_CANT_BE_LOCAL_CLASS!>object<!> : Parcelable {}
object : Open("") {}
@Parcelize
class <!NO_PARCELABLE_SUPERTYPE, PARCELABLE_CANT_BE_LOCAL_CLASS!>Local<!> {}
}