diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeAnnotationChecker.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeAnnotationChecker.kt index b3c715b5d22..2e208b8fe22 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeAnnotationChecker.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeAnnotationChecker.kt @@ -86,7 +86,9 @@ open class ParcelizeAnnotationChecker : CallChecker { private fun checkIgnoredOnParcelUsage(annotationEntry: KtAnnotationEntry, context: CallCheckerContext, element: KtModifierListOwner) { if (element is KtParameter && PsiTreeUtil.getParentOfType(element, KtDeclaration::class.java) is KtPrimaryConstructor) { - context.trace.report(ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY.on(annotationEntry)) + if (!element.hasDefaultValue()) { + context.trace.report(ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY.on(annotationEntry)) + } } else if (element !is KtProperty || PsiTreeUtil.getParentOfType(element, KtDeclaration::class.java) !is KtClassOrObject) { context.trace.report(ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL.on(annotationEntry)) } diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeDeclarationChecker.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeDeclarationChecker.kt index 84b69939fb1..60fa6a402a5 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeDeclarationChecker.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeDeclarationChecker.kt @@ -226,6 +226,12 @@ open class ParcelizeDeclarationChecker : DeclarationChecker { } val descriptor = typeMapper.bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter] ?: return + + // Don't check parameters which won't be serialized + if (descriptor.annotations.any { it.fqName in IGNORED_ON_PARCEL_FQ_NAMES }) { + return + } + val type = descriptor.type if (!type.isError && !containerClass.hasCustomParceler()) { diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/diagnostic/DefaultErrorMessagesParcelize.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/diagnostic/DefaultErrorMessagesParcelize.kt index df513d79693..8948c1f9f07 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/diagnostic/DefaultErrorMessagesParcelize.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/diagnostic/DefaultErrorMessagesParcelize.kt @@ -132,7 +132,7 @@ object DefaultErrorMessagesParcelize : DefaultErrorMessages.Extension { MAP.put( ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY, - "'@IgnoredOnParcel' is inapplicable to properties declared in the primary constructor" + "'@IgnoredOnParcel' is inapplicable to properties without default value declared in the primary constructor" ) MAP.put( diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeConstructorChecker.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeConstructorChecker.kt index 8db67989d1a..ca1a5da8485 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeConstructorChecker.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeConstructorChecker.kt @@ -13,7 +13,8 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirConstructorChecker import org.jetbrains.kotlin.fir.declarations.FirConstructor import org.jetbrains.kotlin.fir.declarations.FirRegularClass -import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR +import org.jetbrains.kotlin.fir.expressions.classId +import org.jetbrains.kotlin.parcelize.ParcelizeNames object FirParcelizeConstructorChecker : FirConstructorChecker() { override fun check(declaration: FirConstructor, context: CheckerContext, reporter: DiagnosticReporter) { @@ -29,7 +30,23 @@ object FirParcelizeConstructorChecker : FirConstructorChecker() { } else { for (valueParameter in declaration.valueParameters) { if (valueParameter.source?.hasValOrVar() != true) { - reporter.reportOn(valueParameter.source, PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR, context) + reporter.reportOn( + valueParameter.source, + KtErrorsParcelize.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR, + context + ) + } + if (valueParameter.defaultValue == null) { + val illegalAnnotation = valueParameter.annotations.firstOrNull { + it.classId in ParcelizeNames.IGNORED_ON_PARCEL_CLASS_IDS + } + if (illegalAnnotation != null) { + reporter.reportOn( + illegalAnnotation.source, + KtErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY, + context + ) + } } } } diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizePropertyChecker.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizePropertyChecker.kt index 51ff53dc813..46e670211cd 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizePropertyChecker.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizePropertyChecker.kt @@ -44,7 +44,6 @@ object FirParcelizePropertyChecker : FirPropertyChecker() { } if (fromPrimaryConstructor) { checkParcelableClassProperty(declaration, containingClassSymbol, context, reporter) - checkIgnoredOnParcelUsage(declaration, context, reporter) } } @@ -56,11 +55,6 @@ object FirParcelizePropertyChecker : FirPropertyChecker() { } } - private fun checkIgnoredOnParcelUsage(property: FirProperty, context: CheckerContext, reporter: DiagnosticReporter) { - val illegalAnnotation = property.annotations.firstOrNull { it.classId in IGNORED_ON_PARCEL_CLASS_IDS } ?: return - reporter.reportOn(illegalAnnotation.source, KtErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY, context) - } - @Suppress("UNUSED_PARAMETER") private fun checkParcelableClassProperty( property: FirProperty, diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/KtDefaultErrorMessagesParcelize.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/KtDefaultErrorMessagesParcelize.kt index cb2688fc969..1cbdb321eb5 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/KtDefaultErrorMessagesParcelize.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/KtDefaultErrorMessagesParcelize.kt @@ -159,7 +159,7 @@ object KtDefaultErrorMessagesParcelize { map.put( INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY, - "'@IgnoredOnParcel' is inapplicable to properties declared in the primary constructor" + "'@IgnoredOnParcel' is inapplicable to properties without default value declared in the primary constructor" ) map.put( diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformerBase.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformerBase.kt index c7ac58fe69e..e4bc74459c4 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformerBase.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformerBase.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATE_FROM_PARCEL_NAME import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATOR_NAME +import org.jetbrains.kotlin.parcelize.ParcelizeNames.IGNORED_ON_PARCEL_FQ_NAMES import org.jetbrains.kotlin.parcelize.ParcelizeNames.NEW_ARRAY_NAME import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase @@ -34,8 +35,8 @@ abstract class ParcelizeIrTransformerBase( protected val deferredOperations = mutableListOf<() -> Unit>() protected fun defer(block: () -> Unit) = deferredOperations.add(block) - protected fun IrSimpleFunction.generateDescribeContentsBody(parcelableProperties: List) { - val flags = if (parcelableProperties.any { it.field.type.containsFileDescriptors }) 1 else 0 + protected fun IrSimpleFunction.generateDescribeContentsBody(parcelableProperties: List) { + val flags = if (parcelableProperties.any { it != null && it.field.type.containsFileDescriptors }) 1 else 0 body = context.createIrBuilder(symbol).run { irExprBody(irInt(flags)) } @@ -44,7 +45,7 @@ abstract class ParcelizeIrTransformerBase( protected fun IrSimpleFunction.generateWriteToParcelBody( irClass: IrClass, parcelerObject: IrClass?, - parcelableProperties: List, + parcelableProperties: List, receiverParameter: IrValueParameter, parcelParameter: IrValueParameter, flagsParameter: IrValueParameter @@ -57,12 +58,14 @@ abstract class ParcelizeIrTransformerBase( parcelableProperties.isNotEmpty() -> for (property in parcelableProperties) { - +writeParcelWith( - property.parceler, - parcelParameter, - flagsParameter, - irGetField(irGet(receiverParameter), property.field) - ) + if (property != null) { + +writeParcelWith( + property.parceler, + parcelParameter, + flagsParameter, + irGetField(irGet(receiverParameter), property.field) + ) + } } else -> @@ -77,7 +80,7 @@ abstract class ParcelizeIrTransformerBase( } } - protected fun generateCreator(declaration: IrClass, parcelerObject: IrClass?, parcelableProperties: List) { + protected fun generateCreator(declaration: IrClass, parcelerObject: IrClass?, parcelableProperties: List) { // Since the `CREATOR` object cannot refer to the type parameters of the parcelable class we use a star projected type val declarationType = declaration.symbol.starProjectedType val creatorType = androidSymbols.androidOsParcelableCreator.typeWith(declarationType) @@ -136,7 +139,9 @@ abstract class ParcelizeIrTransformerBase( parcelableProperties.isNotEmpty() -> irCall(declaration.primaryConstructor!!).apply { for ((index, property) in parcelableProperties.withIndex()) { - putValueArgument(index, readParcelWith(property.parceler, parcelParameter)) + if (property != null) { + putValueArgument(index, readParcelWith(property.parceler, parcelParameter)) + } } } @@ -171,7 +176,7 @@ abstract class ParcelizeIrTransformerBase( private val serializerFactory = IrParcelSerializerFactory(androidSymbols) - protected val IrClass.parcelableProperties: List + protected val IrClass.parcelableProperties: List get() { if (kind != ClassKind.CLASS) return emptyList() @@ -180,9 +185,13 @@ abstract class ParcelizeIrTransformerBase( return constructor.valueParameters.map { parameter -> val property = properties.first { it.name == parameter.name } - val localScope = property.getParcelerScope(topLevelScope) - ParcelableProperty(property.backingField!!) { - serializerFactory.get(parameter.type, parcelizeType = defaultType, scope = localScope) + if (property.hasAnyAnnotation(IGNORED_ON_PARCEL_FQ_NAMES)) { + null + } else { + val localScope = property.getParcelerScope(topLevelScope) + ParcelableProperty(property.backingField!!) { + serializerFactory.get(parameter.type, parcelizeType = defaultType, scope = localScope) + } } } } diff --git a/plugins/parcelize/parcelize-compiler/testData/box/ignoredOnParcel.kt b/plugins/parcelize/parcelize-compiler/testData/box/ignoredOnParcel.kt new file mode 100644 index 00000000000..a939983674b --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/testData/box/ignoredOnParcel.kt @@ -0,0 +1,51 @@ +// WITH_STDLIB +// IGNORE_BACKEND: JVM + +@file:JvmName("TestKt") +package test + +import kotlinx.parcelize.* +import android.os.Parcel +import android.os.Parcelable + +class T(val value: Int) + +@Parcelize +class A( + val x: String, + @IgnoredOnParcel + val i1: String = "default", + @IgnoredOnParcel + val i2: T = T(10), + val d: String = "default", +) : Parcelable { + @IgnoredOnParcel + val a: String = x +} + +@Parcelize +object B : Parcelable { + @IgnoredOnParcel + val x: T = T(2) +} + +fun box() = parcelTest { parcel -> + val a1 = A("X", "i1", T(0), "d") + + a1.writeToParcel(parcel, 0) + B.writeToParcel(parcel, 0) + + val bytes = parcel.marshall() + parcel.unmarshall(bytes, 0, bytes.size) + parcel.setDataPosition(0) + + val a2 = parcelableCreator().createFromParcel(parcel) + val b = parcelableCreator().createFromParcel(parcel) + + assert(a1.x == a2.x) + assert(a2.i1 == "default") + assert(a2.i2.value == 10) + assert(a1.d == a2.d) + assert(a1.a == a2.a) + assert(b == B) +} diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/ignoredOnParcelDefaultValues.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/ignoredOnParcelDefaultValues.kt new file mode 100644 index 00000000000..9da43785b1a --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/ignoredOnParcelDefaultValues.kt @@ -0,0 +1,9 @@ +// FIR_IDENTICAL +package test + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +import kotlinx.parcelize.IgnoredOnParcel + +@Parcelize +class A(@IgnoredOnParcel val x: String = "OK") : Parcelable diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/ignoredOnParcelUnsupportedType.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/ignoredOnParcelUnsupportedType.kt new file mode 100644 index 00000000000..88167d57761 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/ignoredOnParcelUnsupportedType.kt @@ -0,0 +1,16 @@ +// FIR_IDENTICAL +package test + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +import kotlinx.parcelize.IgnoredOnParcel + +class T(val x: Int) + +@Parcelize +class A( + // T is not parcelable, but we don't need it to be since it is not being serialized. + @IgnoredOnParcel val x: T = T(0) +) : Parcelable { + @IgnoredOnParcel val y: T = x +} diff --git a/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/FirParcelizeDiagnosticTestGenerated.java b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/FirParcelizeDiagnosticTestGenerated.java index d27a1e6cd86..55622c94df8 100644 --- a/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/FirParcelizeDiagnosticTestGenerated.java +++ b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/FirParcelizeDiagnosticTestGenerated.java @@ -66,6 +66,18 @@ public class FirParcelizeDiagnosticTestGenerated extends AbstractFirParcelizeDia runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/emptyPrimaryConstructor.kt"); } + @Test + @TestMetadata("ignoredOnParcelDefaultValues.kt") + public void testIgnoredOnParcelDefaultValues() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/ignoredOnParcelDefaultValues.kt"); + } + + @Test + @TestMetadata("ignoredOnParcelUnsupportedType.kt") + public void testIgnoredOnParcelUnsupportedType() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/ignoredOnParcelUnsupportedType.kt"); + } + @Test @TestMetadata("kt20062.kt") public void testKt20062() throws Exception { diff --git a/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeBoxTestGenerated.java b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeBoxTestGenerated.java index 4d15cfe9170..a8d4c17e07f 100644 --- a/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeBoxTestGenerated.java +++ b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeBoxTestGenerated.java @@ -151,6 +151,12 @@ public class ParcelizeBoxTestGenerated extends AbstractParcelizeBoxTest { runTest("plugins/parcelize/parcelize-compiler/testData/box/generics.kt"); } + @Test + @TestMetadata("ignoredOnParcel.kt") + public void testIgnoredOnParcel() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/ignoredOnParcel.kt"); + } + @Test @TestMetadata("intArray.kt") public void testIntArray() throws Exception { diff --git a/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeDiagnosticTestGenerated.java b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeDiagnosticTestGenerated.java index aa5de810efc..7dfbd79afd1 100644 --- a/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeDiagnosticTestGenerated.java +++ b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeDiagnosticTestGenerated.java @@ -66,6 +66,18 @@ public class ParcelizeDiagnosticTestGenerated extends AbstractParcelizeDiagnosti runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/emptyPrimaryConstructor.kt"); } + @Test + @TestMetadata("ignoredOnParcelDefaultValues.kt") + public void testIgnoredOnParcelDefaultValues() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/ignoredOnParcelDefaultValues.kt"); + } + + @Test + @TestMetadata("ignoredOnParcelUnsupportedType.kt") + public void testIgnoredOnParcelUnsupportedType() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/ignoredOnParcelUnsupportedType.kt"); + } + @Test @TestMetadata("kt20062.kt") public void testKt20062() throws Exception { diff --git a/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeFirBoxTestGenerated.java b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeFirBoxTestGenerated.java index 65fc1f209f6..4d4981770e1 100644 --- a/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeFirBoxTestGenerated.java +++ b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeFirBoxTestGenerated.java @@ -151,6 +151,12 @@ public class ParcelizeFirBoxTestGenerated extends AbstractParcelizeFirBoxTest { runTest("plugins/parcelize/parcelize-compiler/testData/box/generics.kt"); } + @Test + @TestMetadata("ignoredOnParcel.kt") + public void testIgnoredOnParcel() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/ignoredOnParcel.kt"); + } + @Test @TestMetadata("intArray.kt") public void testIntArray() throws Exception { diff --git a/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeIrBoxTestGenerated.java b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeIrBoxTestGenerated.java index 8dcbfeed86d..1444fb1be2a 100644 --- a/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeIrBoxTestGenerated.java +++ b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeIrBoxTestGenerated.java @@ -151,6 +151,12 @@ public class ParcelizeIrBoxTestGenerated extends AbstractParcelizeIrBoxTest { runTest("plugins/parcelize/parcelize-compiler/testData/box/generics.kt"); } + @Test + @TestMetadata("ignoredOnParcel.kt") + public void testIgnoredOnParcel() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/ignoredOnParcel.kt"); + } + @Test @TestMetadata("intArray.kt") public void testIntArray() throws Exception {