Parcelize: Skip constructor checks when used with custom Parceler

See https://issuetracker.google.com/177850558
This commit is contained in:
Steven Schäfer
2021-12-03 15:22:01 +01:00
committed by Alexander Udalov
parent 58c687e42d
commit 16a2aec296
11 changed files with 134 additions and 5 deletions
@@ -99,6 +99,7 @@ open class ParcelizeDeclarationChecker : DeclarationChecker {
if (containingClass.isParcelize
&& (declaration.hasDelegate() || bindingContext[BindingContext.BACKING_FIELD_REQUIRED, property] == true)
&& !hasIgnoredOnParcel()
&& !containingClass.hasCustomParceler()
) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.report(ErrorsParcelize.PROPERTY_WONT_BE_SERIALIZED.on(reportElement))
@@ -192,6 +193,11 @@ open class ParcelizeDeclarationChecker : DeclarationChecker {
}
}
// The constructor checks are irrelevant for custom parcelers
if (descriptor.hasCustomParceler()) {
return
}
val primaryConstructor = declaration.primaryConstructor
if (primaryConstructor == null && declaration.secondaryConstructors.isNotEmpty()) {
val reportElement = declaration.nameIdentifier ?: declaration
@@ -234,7 +240,7 @@ open class ParcelizeDeclarationChecker : DeclarationChecker {
val type = descriptor.type
if (!type.isError && !containerClass.hasCustomParceler()) {
if (!type.isError) {
val asmType = typeMapper.mapType(type, mode = TypeMappingMode.CLASS_DECLARATION)
try {
@@ -32,6 +32,7 @@ object ParcelizeNames {
val TYPE_PARCELER_CLASS_IDS = createClassIds("TypeParceler")
val WRITE_WITH_CLASS_IDS = createClassIds("WriteWith")
val IGNORED_ON_PARCEL_CLASS_IDS = createClassIds("IgnoredOnParcel")
val PARCELER_CLASS_IDS = createClassIds("Parceler")
val PARCELIZE_CLASS_CLASS_IDS = createClassIds("Parcelize")
val RAW_VALUE_ANNOTATION_CLASS_IDS = createClassIds("RawValue")
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATOR_NAME
import org.jetbrains.kotlin.parcelize.ParcelizeNames.OLD_PARCELER_ID
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELABLE_ID
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELER_CLASS_IDS
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELIZE_CLASS_CLASS_IDS
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@@ -131,3 +132,10 @@ fun FirClassSymbol<*>?.isParcelize(session: FirSession): Boolean {
symbol.annotations.any { it.classId in PARCELIZE_CLASS_CLASS_IDS }
}
}
fun FirRegularClass.hasCustomParceler(session: FirSession): Boolean {
val companion = companionObjectSymbol ?: return false
return lookupSuperTypes(companion, lookupInterfaces = true, deep = true, useSiteSession = session).any {
it.classId in PARCELER_CLASS_IDS
}
}
@@ -23,7 +23,7 @@ object FirParcelizeConstructorChecker : FirConstructorChecker() {
if (source.kind == KtFakeSourceElementKind.ImplicitConstructor) return
val containingClass = context.containingDeclarations.last() as? FirRegularClass ?: return
val containingClassSymbol = containingClass.symbol
if (!containingClassSymbol.isParcelize(context.session)) return
if (!containingClassSymbol.isParcelize(context.session) || containingClass.hasCustomParceler(context.session)) return
if (declaration.valueParameters.isEmpty()) {
reporter.reportOn(containingClass.source, KtErrorsParcelize.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY, context)
@@ -38,7 +38,8 @@ object FirParcelizePropertyChecker : FirPropertyChecker() {
if (
!fromPrimaryConstructor &&
(declaration.hasBackingField || declaration.delegate != null) &&
!declaration.hasIgnoredOnParcel()
!declaration.hasIgnoredOnParcel() &&
!containingClassSymbol.hasCustomParceler(context.session)
) {
reporter.reportOn(declaration.source, KtErrorsParcelize.PROPERTY_WONT_BE_SERIALIZED, context)
}
@@ -184,8 +184,8 @@ abstract class ParcelizeIrTransformerBase(
val topLevelScope = getParcelerScope()
return constructor.valueParameters.map { parameter ->
val property = properties.first { it.name == parameter.name }
if (property.hasAnyAnnotation(IGNORED_ON_PARCEL_FQ_NAMES)) {
val property = properties.firstOrNull { it.name == parameter.name }
if (property == null || property.hasAnyAnnotation(IGNORED_ON_PARCEL_FQ_NAMES)) {
null
} else {
val localScope = property.getParcelerScope(topLevelScope)
@@ -0,0 +1,35 @@
// WITH_STDLIB
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
class A(_a: String) : Parcelable {
var a: String = _a
private set
companion object : Parceler<A> {
override fun A.write(parcel: Parcel, flags: Int) {
parcel.writeString(a)
}
override fun create(parcel: Parcel) = A(parcel.readString())
}
}
fun box() = parcelTest { parcel ->
val test = A("OK")
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = parcelableCreator<A>().createFromParcel(parcel)
assert(test.a == test2.a)
}
@@ -0,0 +1,42 @@
// WITH_STDLIB
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
class T(val value: String)
// There's no warnings on empty constructors, secondary constructors, or
// non-parcelable types if there is a custom parceler.
@Parcelize
class A() : Parcelable {
var a: T = T("Fail")
constructor(value: String) : this() {
a = T(value)
}
companion object : Parceler<A> {
override fun A.write(parcel: Parcel, flags: Int) {
parcel.writeString(a.value)
}
override fun create(parcel: Parcel) = A(parcel.readString())
}
}
fun box() = parcelTest { parcel ->
val test = A("OK")
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = parcelableCreator<A>().createFromParcel(parcel)
assert(test.a.value == test2.a.value)
}
@@ -73,6 +73,12 @@ public class ParcelizeBoxTestGenerated extends AbstractParcelizeBoxTest {
runTest("plugins/parcelize/parcelize-compiler/testData/box/charSequence.kt");
}
@Test
@TestMetadata("constructorWithoutValOrVar.kt")
public void testConstructorWithoutValOrVar() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/box/constructorWithoutValOrVar.kt");
}
@Test
@TestMetadata("customNewArray.kt")
public void testCustomNewArray() throws Exception {
@@ -85,6 +91,12 @@ public class ParcelizeBoxTestGenerated extends AbstractParcelizeBoxTest {
runTest("plugins/parcelize/parcelize-compiler/testData/box/customParcelable.kt");
}
@Test
@TestMetadata("customParcelerChecks.kt")
public void testCustomParcelerChecks() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/box/customParcelerChecks.kt");
}
@Test
@TestMetadata("customParcelerScoping.kt")
public void testCustomParcelerScoping() throws Exception {
@@ -73,6 +73,12 @@ public class ParcelizeFirBoxTestGenerated extends AbstractParcelizeFirBoxTest {
runTest("plugins/parcelize/parcelize-compiler/testData/box/charSequence.kt");
}
@Test
@TestMetadata("constructorWithoutValOrVar.kt")
public void testConstructorWithoutValOrVar() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/box/constructorWithoutValOrVar.kt");
}
@Test
@TestMetadata("customNewArray.kt")
public void testCustomNewArray() throws Exception {
@@ -85,6 +91,12 @@ public class ParcelizeFirBoxTestGenerated extends AbstractParcelizeFirBoxTest {
runTest("plugins/parcelize/parcelize-compiler/testData/box/customParcelable.kt");
}
@Test
@TestMetadata("customParcelerChecks.kt")
public void testCustomParcelerChecks() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/box/customParcelerChecks.kt");
}
@Test
@TestMetadata("customParcelerScoping.kt")
public void testCustomParcelerScoping() throws Exception {
@@ -73,6 +73,12 @@ public class ParcelizeIrBoxTestGenerated extends AbstractParcelizeIrBoxTest {
runTest("plugins/parcelize/parcelize-compiler/testData/box/charSequence.kt");
}
@Test
@TestMetadata("constructorWithoutValOrVar.kt")
public void testConstructorWithoutValOrVar() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/box/constructorWithoutValOrVar.kt");
}
@Test
@TestMetadata("customNewArray.kt")
public void testCustomNewArray() throws Exception {
@@ -85,6 +91,12 @@ public class ParcelizeIrBoxTestGenerated extends AbstractParcelizeIrBoxTest {
runTest("plugins/parcelize/parcelize-compiler/testData/box/customParcelable.kt");
}
@Test
@TestMetadata("customParcelerChecks.kt")
public void testCustomParcelerChecks() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/box/customParcelerChecks.kt");
}
@Test
@TestMetadata("customParcelerScoping.kt")
public void testCustomParcelerScoping() throws Exception {