From fa0d456850eb9348476fe110167aa27dcce595ec Mon Sep 17 00:00:00 2001 From: Mads Ager Date: Mon, 26 Feb 2024 14:29:55 +0100 Subject: [PATCH] [Parcelize] Allow parcelize to work on common code in multiplatform. Since parcelize is Android specific, it should only be enabled for Android compilation. In order to allow parcelize to generate code for declarations in common code, we make checkers platform checkers and we allow the registration of an additional annotations to trigger parcelize processing. That way, code such as: ``` package my.package annotation class Parcelize expect interface MyParcelable @Parcelize data class User(name: String): MyParcelable ``` Will work with an Android platform actual of the form: ``` actual typealias MyParcelable = android.os.Parcelable ``` And telling the plugin to trigger parcelize processing with the additional annotation `my.package.Parcelize`. Fixes https://issuetracker.google.com/315775835. --- .../parcelize/IrParcelSerializerFactory.kt | 5 ++- .../ParcelizeFirIrGeneratorExtension.kt | 5 ++- .../parcelize/ParcelizeFirIrTransformer.kt | 8 ++-- .../ParcelizeIrGeneratorExtension.kt | 6 ++- .../parcelize/ParcelizeIrTransformer.kt | 7 ++-- .../parcelize/ParcelizeIrTransformerBase.kt | 6 ++- .../org/jetbrains/kotlin/parcelize/irUtils.kt | 9 ++--- ...otlin.compiler.plugin.CommandLineProcessor | 1 + .../ParcelizeCommandLineProcessor.kt | 40 +++++++++++++++++++ .../parcelize/ParcelizeComponentRegistrar.kt | 30 +++++++++----- .../parcelize/ParcelizeConfigurationKeys.kt | 13 ++++++ .../kotlin/parcelize/ParcelizeNames.kt | 4 -- .../parcelize/ParcelizeAnnotationChecker.kt | 8 ++-- .../parcelize/ParcelizeDeclarationChecker.kt | 10 ++--- .../parcelize/ParcelizeResolveExtension.kt | 21 +++++----- .../parcelize/serializers/ParcelSerializer.kt | 3 +- .../serializers/ParcelizeExtensionBase.kt | 3 +- .../fir/FirParcelizeCheckersExtension.kt | 16 +++++--- .../fir/FirParcelizeDeclarationGenerator.kt | 15 ++++--- .../fir/FirParcelizeExtensionRegistrar.kt | 8 ++-- .../FirParcelizeAnnotationChecker.kt | 8 ++-- .../diagnostics/FirParcelizeClassChecker.kt | 10 ++--- .../FirParcelizeConstructorChecker.kt | 8 +++- .../FirParcelizeFunctionChecker.kt | 5 ++- .../FirParcelizePropertyChecker.kt | 7 ++-- .../ParcelizeEnvironmentConfigurator.kt | 8 +++- 26 files changed, 177 insertions(+), 87 deletions(-) create mode 100644 plugins/parcelize/parcelize-compiler/parcelize.cli/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor create mode 100644 plugins/parcelize/parcelize-compiler/parcelize.cli/src/org/jetbrains/kotlin/parcelize/ParcelizeCommandLineProcessor.kt create mode 100644 plugins/parcelize/parcelize-compiler/parcelize.cli/src/org/jetbrains/kotlin/parcelize/ParcelizeConfigurationKeys.kt diff --git a/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/IrParcelSerializerFactory.kt b/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/IrParcelSerializerFactory.kt index 8867dec2941..ef6a88007f2 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/IrParcelSerializerFactory.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/IrParcelSerializerFactory.kt @@ -12,9 +12,10 @@ import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.declarations.inlineClassRepresentation import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.parcelize.ParcelizeNames.RAW_VALUE_ANNOTATION_FQ_NAMES -class IrParcelSerializerFactory(private val symbols: AndroidSymbols) { +class IrParcelSerializerFactory(private val symbols: AndroidSymbols, private val parcelizeAnnotations: List) { private val supportedBySimpleListSerializer = setOf( "kotlin.collections.List", "kotlin.collections.MutableList", "kotlin.collections.ArrayList", "java.util.List", "java.util.ArrayList", @@ -313,7 +314,7 @@ class IrParcelSerializerFactory(private val symbols: AndroidSymbols) { // @Parcelize), we'll have a field in the class itself. Finally, with Parcelable instances which were // manually implemented in Kotlin, we'll instead have an @JvmField property getter in the companion object. return if (classifier.modality == Modality.FINAL && classifier.psiElement != null - && (classifier.isParcelize || classifier.hasCreatorField) + && (classifier.isParcelize(parcelizeAnnotations) || classifier.hasCreatorField) ) { wrapNullableSerializerIfNeeded(irType, IrEfficientParcelableParcelSerializer(classifier)) } else { diff --git a/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeFirIrGeneratorExtension.kt b/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeFirIrGeneratorExtension.kt index 3d80d896eed..c9563da5611 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeFirIrGeneratorExtension.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeFirIrGeneratorExtension.kt @@ -8,10 +8,11 @@ package org.jetbrains.kotlin.parcelize import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.name.FqName -class ParcelizeFirIrGeneratorExtension : IrGenerationExtension { +class ParcelizeFirIrGeneratorExtension(private val parcelizeAnnotations: List) : IrGenerationExtension { override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) { val androidSymbols = AndroidSymbols(pluginContext, moduleFragment) - ParcelizeFirIrTransformer(pluginContext, androidSymbols).transform(moduleFragment) + ParcelizeFirIrTransformer(pluginContext, androidSymbols, parcelizeAnnotations).transform(moduleFragment) } } diff --git a/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeFirIrTransformer.kt b/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeFirIrTransformer.kt index 082c842a2a0..e8ea6e82d6c 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeFirIrTransformer.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeFirIrTransformer.kt @@ -14,13 +14,15 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.util.companionObject import org.jetbrains.kotlin.ir.util.functions import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELER_FQN import org.jetbrains.kotlin.parcelize.fir.ParcelizePluginKey class ParcelizeFirIrTransformer( context: IrPluginContext, - androidSymbols: AndroidSymbols -) : ParcelizeIrTransformerBase(context, androidSymbols) { + androidSymbols: AndroidSymbols, + parcelizeAnnotations: List +) : ParcelizeIrTransformerBase(context, androidSymbols, parcelizeAnnotations) { fun transform(moduleFragment: IrModuleFragment) { moduleFragment.accept(this, null) @@ -34,7 +36,7 @@ class ParcelizeFirIrTransformer( // Sealed classes can be annotated with `@Parcelize`, but that only implies that we // should process their immediate subclasses. - if (!declaration.isParcelize || declaration.modality == Modality.SEALED) + if (!declaration.isParcelize(parcelizeAnnotations) || declaration.modality == Modality.SEALED) return val parcelableProperties = declaration.parcelableProperties diff --git a/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeIrGeneratorExtension.kt b/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeIrGeneratorExtension.kt index 2098b99c288..48f46526a0b 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeIrGeneratorExtension.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeIrGeneratorExtension.kt @@ -8,10 +8,12 @@ package org.jetbrains.kotlin.parcelize import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.name.FqName -class ParcelizeIrGeneratorExtension : IrGenerationExtension { +// This class is open so that the IDE integration can create a subclass with a fixed set of annotations. +open class ParcelizeIrGeneratorExtension(private val parcelizeAnnotations: List) : IrGenerationExtension { override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) { val androidSymbols = AndroidSymbols(pluginContext, moduleFragment) - ParcelizeIrTransformer(pluginContext, androidSymbols).transform(moduleFragment) + ParcelizeIrTransformer(pluginContext, androidSymbols, parcelizeAnnotations).transform(moduleFragment) } } diff --git a/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeIrTransformer.kt b/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeIrTransformer.kt index 802d3ed88f5..ce6b4b366c3 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeIrTransformer.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeIrTransformer.kt @@ -36,8 +36,9 @@ import org.jetbrains.kotlin.parcelize.ParcelizeNames.WRITE_TO_PARCEL_NAME @OptIn(ObsoleteDescriptorBasedAPI::class) class ParcelizeIrTransformer( context: IrPluginContext, - androidSymbols: AndroidSymbols -) : ParcelizeIrTransformerBase(context, androidSymbols) { + androidSymbols: AndroidSymbols, + parcelizeAnnotations: List +) : ParcelizeIrTransformerBase(context, androidSymbols, parcelizeAnnotations) { private val symbolMap = mutableMapOf() fun transform(moduleFragment: IrModuleFragment) { @@ -101,7 +102,7 @@ class ParcelizeIrTransformer( // Sealed classes can be annotated with `@Parcelize`, but that only implies that we // should process their immediate subclasses. - if (!declaration.isParcelize || declaration.modality == Modality.SEALED) + if (!declaration.isParcelize(parcelizeAnnotations) || declaration.modality == Modality.SEALED) return val parcelableProperties = declaration.parcelableProperties diff --git a/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeIrTransformerBase.kt b/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeIrTransformerBase.kt index 10de5abe405..56fd77e4053 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeIrTransformerBase.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/ParcelizeIrTransformerBase.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATE_FROM_PARCEL_NAME import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATOR_NAME @@ -27,7 +28,8 @@ import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase abstract class ParcelizeIrTransformerBase( protected val context: IrPluginContext, - protected val androidSymbols: AndroidSymbols + protected val androidSymbols: AndroidSymbols, + protected val parcelizeAnnotations: List ) : ParcelizeExtensionBase, IrElementVisitorVoid { private val irFactory: IrFactory = IrFactoryImpl @@ -173,7 +175,7 @@ abstract class ParcelizeIrTransformerBase( val parceler by lazy(parcelerThunk) } - private val serializerFactory = IrParcelSerializerFactory(androidSymbols) + private val serializerFactory = IrParcelSerializerFactory(androidSymbols, parcelizeAnnotations) protected val IrClass.parcelableProperties: List get() { diff --git a/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/irUtils.kt b/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/irUtils.kt index 1da35f25f64..5d978172343 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/irUtils.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.backend/src/org/jetbrains/kotlin/parcelize/irUtils.kt @@ -27,17 +27,16 @@ import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATOR_NAME import org.jetbrains.kotlin.parcelize.ParcelizeNames.NEW_ARRAY_NAME import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELABLE_FQN import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELER_FQN -import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELIZE_CLASS_FQ_NAMES import org.jetbrains.kotlin.parcelize.ParcelizeNames.WRITE_TO_PARCEL_NAME import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase import org.jetbrains.kotlin.types.Variance // true if the class should be processed by the parcelize plugin -val IrClass.isParcelize: Boolean - get() = kind in ParcelizeExtensionBase.ALLOWED_CLASS_KINDS && - (hasAnyAnnotation(PARCELIZE_CLASS_FQ_NAMES) || superTypes.any { superType -> +fun IrClass.isParcelize(parcelizeAnnotations: List): Boolean = + kind in ParcelizeExtensionBase.ALLOWED_CLASS_KINDS && + (hasAnyAnnotation(parcelizeAnnotations) || superTypes.any { superType -> superType.classOrNull?.owner?.let { - it.modality == Modality.SEALED && it.hasAnyAnnotation(PARCELIZE_CLASS_FQ_NAMES) + it.modality == Modality.SEALED && it.hasAnyAnnotation(parcelizeAnnotations) } == true }) diff --git a/plugins/parcelize/parcelize-compiler/parcelize.cli/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor b/plugins/parcelize/parcelize-compiler/parcelize.cli/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor new file mode 100644 index 00000000000..2488a0bcdd1 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/parcelize.cli/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor @@ -0,0 +1 @@ +org.jetbrains.kotlin.parcelize.ParcelizeCommandLineProcessor \ No newline at end of file diff --git a/plugins/parcelize/parcelize-compiler/parcelize.cli/src/org/jetbrains/kotlin/parcelize/ParcelizeCommandLineProcessor.kt b/plugins/parcelize/parcelize-compiler/parcelize.cli/src/org/jetbrains/kotlin/parcelize/ParcelizeCommandLineProcessor.kt new file mode 100644 index 00000000000..af01707a153 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/parcelize.cli/src/org/jetbrains/kotlin/parcelize/ParcelizeCommandLineProcessor.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.parcelize + +import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption +import org.jetbrains.kotlin.compiler.plugin.CliOption +import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException +import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor +import org.jetbrains.kotlin.config.CompilerConfiguration + +class ParcelizeCommandLineProcessor : CommandLineProcessor { + companion object { + const val COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.parcelize" + + val ADDITIONAL_ANNOTATION_OPTION: CliOption = + CliOption( + "additionalAnnotation", + "", + "Additional annotation to trigger parcelize processing.", + required = false, + allowMultipleOccurrences = true + ) + } + + override val pluginId: String + get() = COMPILER_PLUGIN_ID + + override val pluginOptions: Collection + get() = listOf(ADDITIONAL_ANNOTATION_OPTION) + + override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) { + when (option) { + ADDITIONAL_ANNOTATION_OPTION -> configuration.appendList(ParcelizeConfigurationKeys.ADDITIONAL_ANNOTATION, value) + else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}") + } + } +} \ No newline at end of file diff --git a/plugins/parcelize/parcelize-compiler/parcelize.cli/src/org/jetbrains/kotlin/parcelize/ParcelizeComponentRegistrar.kt b/plugins/parcelize/parcelize-compiler/parcelize.cli/src/org/jetbrains/kotlin/parcelize/ParcelizeComponentRegistrar.kt index 95d34be02d7..578a13e3713 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.cli/src/org/jetbrains/kotlin/parcelize/ParcelizeComponentRegistrar.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.cli/src/org/jetbrains/kotlin/parcelize/ParcelizeComponentRegistrar.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.container.useInstance import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.parcelize.fir.FirParcelizeExtensionRegistrar import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.jvm.isJvm @@ -21,35 +22,44 @@ import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension class ParcelizeComponentRegistrar : CompilerPluginRegistrar() { companion object { - fun registerParcelizeComponents(extensionStorage: ExtensionStorage, useFir: Boolean) = with(extensionStorage) { + fun registerParcelizeComponents( + extensionStorage: ExtensionStorage, + additionalAnnotation: List, + useFir: Boolean + ) = with(extensionStorage) { + val parcelizeAnnotations = ParcelizeNames.PARCELIZE_CLASS_FQ_NAMES.toMutableList() + additionalAnnotation.mapTo(parcelizeAnnotations) { FqName(it) } if (useFir) { - IrGenerationExtension.registerExtension(ParcelizeFirIrGeneratorExtension()) + IrGenerationExtension.registerExtension(ParcelizeFirIrGeneratorExtension(parcelizeAnnotations)) } else { - IrGenerationExtension.registerExtension(ParcelizeIrGeneratorExtension()) + IrGenerationExtension.registerExtension(ParcelizeIrGeneratorExtension(parcelizeAnnotations)) } - SyntheticResolveExtension.registerExtension(ParcelizeResolveExtension()) - StorageComponentContainerContributor.registerExtension(ParcelizeDeclarationCheckerComponentContainerContributor()) - FirExtensionRegistrarAdapter.registerExtension(FirParcelizeExtensionRegistrar()) + SyntheticResolveExtension.registerExtension(ParcelizeResolveExtension(parcelizeAnnotations)) + StorageComponentContainerContributor.registerExtension(ParcelizeDeclarationCheckerComponentContainerContributor(parcelizeAnnotations)) + FirExtensionRegistrarAdapter.registerExtension(FirParcelizeExtensionRegistrar(parcelizeAnnotations)) } } override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) { - registerParcelizeComponents(this, configuration.getBoolean(CommonConfigurationKeys.USE_FIR)) + val additionalAnnotation = configuration.get(ParcelizeConfigurationKeys.ADDITIONAL_ANNOTATION) ?: emptyList() + registerParcelizeComponents(this, additionalAnnotation, configuration.getBoolean(CommonConfigurationKeys.USE_FIR)) } override val supportsK2: Boolean get() = true } -class ParcelizeDeclarationCheckerComponentContainerContributor : StorageComponentContainerContributor { +class ParcelizeDeclarationCheckerComponentContainerContributor( + private val parcelizeAnnotations: List +) : StorageComponentContainerContributor { override fun registerModuleComponents( container: StorageComponentContainer, platform: TargetPlatform, moduleDescriptor: ModuleDescriptor, ) { if (platform.isJvm()) { - container.useInstance(ParcelizeDeclarationChecker()) - container.useInstance(ParcelizeAnnotationChecker()) + container.useInstance(ParcelizeDeclarationChecker(parcelizeAnnotations)) + container.useInstance(ParcelizeAnnotationChecker(parcelizeAnnotations)) } } } diff --git a/plugins/parcelize/parcelize-compiler/parcelize.cli/src/org/jetbrains/kotlin/parcelize/ParcelizeConfigurationKeys.kt b/plugins/parcelize/parcelize-compiler/parcelize.cli/src/org/jetbrains/kotlin/parcelize/ParcelizeConfigurationKeys.kt new file mode 100644 index 00000000000..44e6d7d531f --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/parcelize.cli/src/org/jetbrains/kotlin/parcelize/ParcelizeConfigurationKeys.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.parcelize + +import org.jetbrains.kotlin.config.CompilerConfigurationKey + +object ParcelizeConfigurationKeys { + val ADDITIONAL_ANNOTATION: CompilerConfigurationKey> = + CompilerConfigurationKey.create(ParcelizeCommandLineProcessor.ADDITIONAL_ANNOTATION_OPTION.description) +} \ No newline at end of file diff --git a/plugins/parcelize/parcelize-compiler/parcelize.common/src/org/jetbrains/kotlin/parcelize/ParcelizeNames.kt b/plugins/parcelize/parcelize-compiler/parcelize.common/src/org/jetbrains/kotlin/parcelize/ParcelizeNames.kt index 1f6a41e7252..3b09d0581c6 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.common/src/org/jetbrains/kotlin/parcelize/ParcelizeNames.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.common/src/org/jetbrains/kotlin/parcelize/ParcelizeNames.kt @@ -21,8 +21,6 @@ object ParcelizeNames { // -------------------- Class ids -------------------- - val PARCELIZE_ID = ClassId(FqName("kotlinx.parcelize"), Name.identifier("Parcelize")) - val OLD_PARCELIZE_ID = ClassId(FqName("kotlinx.android.parcel"), Name.identifier("Parcelize")) val PARCEL_ID = ClassId(FqName("android.os"), Name.identifier("Parcel")) val PARCELABLE_ID = ClassId(FqName("android.os"), Name.identifier("Parcelable")) val CREATOR_ID = PARCELABLE_ID.createNestedClassId(Name.identifier("Creator")) @@ -38,8 +36,6 @@ object ParcelizeNames { // -------------------- FQNs -------------------- - val PARCELIZE_FQN = PARCELIZE_ID.asSingleFqName() - val OLD_PARCELIZE_FQN = OLD_PARCELIZE_ID.asSingleFqName() val PARCELABLE_FQN = PARCELABLE_ID.asSingleFqName() val CREATOR_FQN = CREATOR_ID.asSingleFqName() diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/ParcelizeAnnotationChecker.kt b/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/ParcelizeAnnotationChecker.kt index 2e208b8fe22..6d978c542c4 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/ParcelizeAnnotationChecker.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/ParcelizeAnnotationChecker.kt @@ -23,10 +23,10 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.containingPackage +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.parcelize.ParcelizeNames.DEPRECATED_RUNTIME_PACKAGE import org.jetbrains.kotlin.parcelize.ParcelizeNames.IGNORED_ON_PARCEL_FQ_NAMES import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELER_FQN -import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELIZE_CLASS_FQ_NAMES import org.jetbrains.kotlin.parcelize.ParcelizeNames.RAW_VALUE_ANNOTATION_FQ_NAMES import org.jetbrains.kotlin.parcelize.ParcelizeNames.TYPE_PARCELER_FQ_NAMES import org.jetbrains.kotlin.parcelize.ParcelizeNames.WRITE_WITH_FQ_NAMES @@ -43,7 +43,7 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations import org.jetbrains.kotlin.types.typeUtil.supertypes -open class ParcelizeAnnotationChecker : CallChecker { +open class ParcelizeAnnotationChecker(val parcelizeAnnotations : List) : CallChecker { override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { val constructorDescriptor = resolvedCall.resultingDescriptor as? ClassConstructorDescriptor ?: return val annotationClass = constructorDescriptor.constructedClass.takeIf { it.kind == ClassKind.ANNOTATION_CLASS } ?: return @@ -66,7 +66,7 @@ open class ParcelizeAnnotationChecker : CallChecker { checkDeprecatedAnnotations(resolvedCall, annotationEntry, context, isForbidden = false) } - if (annotationClass.fqNameSafe in PARCELIZE_CLASS_FQ_NAMES || annotationClass.fqNameSafe in RAW_VALUE_ANNOTATION_FQ_NAMES) { + if (annotationClass.fqNameSafe in parcelizeAnnotations || annotationClass.fqNameSafe in RAW_VALUE_ANNOTATION_FQ_NAMES) { checkDeprecatedAnnotations(resolvedCall, annotationEntry, context, isForbidden = false) } } @@ -177,7 +177,7 @@ open class ParcelizeAnnotationChecker : CallChecker { ) { if (containingClass != null) { val containingClassDescriptor = context.trace[BindingContext.CLASS, containingClass] - if (containingClassDescriptor != null && !containingClassDescriptor.isParcelize) { + if (containingClassDescriptor != null && !containingClassDescriptor.isParcelize(parcelizeAnnotations)) { val reportElement = (annotationEntry.typeReference?.typeElement as? KtUserType)?.referenceExpression ?: annotationEntry context.trace.report(ErrorsParcelize.CLASS_SHOULD_BE_PARCELIZE.on(reportElement, containingClass)) } diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/ParcelizeDeclarationChecker.kt b/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/ParcelizeDeclarationChecker.kt index 4496221a096..1188a3a8376 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/ParcelizeDeclarationChecker.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/ParcelizeDeclarationChecker.kt @@ -32,7 +32,7 @@ import org.jetbrains.kotlin.types.isError import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound import org.jetbrains.kotlin.types.typeUtil.supertypes -open class ParcelizeDeclarationChecker : DeclarationChecker { +open class ParcelizeDeclarationChecker(val parcelizeAnnotations: List) : DeclarationChecker { private companion object { private val IGNORED_ON_PARCEL_FQ_NAMES = listOf( FqName("kotlinx.parcelize.IgnoredOnParcel"), @@ -71,7 +71,7 @@ open class ParcelizeDeclarationChecker : DeclarationChecker { declaration: KtFunction, diagnosticHolder: DiagnosticSink ) { - if (!containingClass.isParcelize) { + if (!containingClass.isParcelize(parcelizeAnnotations)) { return } @@ -97,7 +97,7 @@ open class ParcelizeDeclarationChecker : DeclarationChecker { return property.annotations.hasIgnoredOnParcel() || (property.getter?.annotations?.hasIgnoredOnParcel() ?: false) } - if (containingClass.isParcelize + if (containingClass.isParcelize(parcelizeAnnotations) && (declaration.hasDelegate() || bindingContext[BindingContext.BACKING_FIELD_REQUIRED, property] == true) && !hasIgnoredOnParcel() && !containingClass.hasCustomParceler() @@ -109,7 +109,7 @@ open class ParcelizeDeclarationChecker : DeclarationChecker { // @JvmName is not applicable to property so we can check just the descriptor name if (property.name.asString() == "CREATOR" && property.findJvmFieldAnnotation() != null && containingClass.isCompanionObject) { val outerClass = containingClass.containingDeclaration as? ClassDescriptor - if (outerClass != null && outerClass.isParcelize) { + if (outerClass != null && outerClass.isParcelize(parcelizeAnnotations)) { val reportElement = declaration.nameIdentifier ?: declaration diagnosticHolder.report(ErrorsParcelize.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement)) } @@ -141,7 +141,7 @@ open class ParcelizeDeclarationChecker : DeclarationChecker { bindingContext: BindingContext, languageVersionSettings: LanguageVersionSettings ) { - if (!descriptor.isParcelize) { + if (!descriptor.isParcelize(parcelizeAnnotations)) { return } diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/ParcelizeResolveExtension.kt b/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/ParcelizeResolveExtension.kt index 1baf0ff9144..c8166ee4f83 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/ParcelizeResolveExtension.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/ParcelizeResolveExtension.kt @@ -30,7 +30,6 @@ import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATE_FROM_PARCEL_NAME import org.jetbrains.kotlin.parcelize.ParcelizeNames.DESCRIBE_CONTENTS_NAME import org.jetbrains.kotlin.parcelize.ParcelizeNames.NEW_ARRAY_NAME import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELER_FQN -import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELIZE_CLASS_FQ_NAMES import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCEL_ID import org.jetbrains.kotlin.parcelize.ParcelizeNames.WRITE_TO_PARCEL_NAME import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS @@ -46,7 +45,7 @@ import org.jetbrains.kotlin.types.SimpleType import org.jetbrains.kotlin.types.error.ErrorTypeKind import org.jetbrains.kotlin.types.error.ErrorUtils -open class ParcelizeResolveExtension : SyntheticResolveExtension { +open class ParcelizeResolveExtension(private val parcelizeAnnotations: List) : SyntheticResolveExtension { companion object { fun resolveParcelClassType(module: ModuleDescriptor): SimpleType? { return module.findClassAcrossModuleDependencies(PARCEL_ID)?.defaultType @@ -107,7 +106,7 @@ open class ParcelizeResolveExtension : SyntheticResolveExtension { override fun getSyntheticCompanionObjectNameIfNeeded(thisDescriptor: ClassDescriptor): Name? = null override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List { - return if (thisDescriptor.isParcelize) parcelizeMethodNames else emptyList() + return if (thisDescriptor.isParcelize(parcelizeAnnotations)) parcelizeMethodNames else emptyList() } override fun generateSyntheticMethods( @@ -123,7 +122,7 @@ open class ParcelizeResolveExtension : SyntheticResolveExtension { } if (name.asString() == DESCRIBE_CONTENTS.methodName - && thisDescriptor.isParcelize + && thisDescriptor.isParcelize(parcelizeAnnotations) && !thisDescriptor.isSealed() && isParcelizePluginEnabled() && result.none { it.isDescribeContents() } @@ -131,7 +130,7 @@ open class ParcelizeResolveExtension : SyntheticResolveExtension { ) { result += createMethod(thisDescriptor, DESCRIBE_CONTENTS, Modality.OPEN, thisDescriptor.builtIns.intType) } else if (name.asString() == WRITE_TO_PARCEL.methodName - && thisDescriptor.isParcelize + && thisDescriptor.isParcelize(parcelizeAnnotations) && !thisDescriptor.isSealed() && isParcelizePluginEnabled() && result.none { it.isWriteToParcel() } @@ -173,13 +172,13 @@ interface ParcelizeSyntheticComponent { } } -val ClassDescriptor.hasParcelizeAnnotation: Boolean - get() = PARCELIZE_CLASS_FQ_NAMES.any(annotations::hasAnnotation) +fun ClassDescriptor.hasParcelizeAnnotation(parcelizeAnnotations: List): Boolean = + parcelizeAnnotations.any(annotations::hasAnnotation) -val ClassDescriptor.isParcelize: Boolean - get() = hasParcelizeAnnotation - || getSuperClassNotAny()?.takeIf(DescriptorUtils::isSealedClass)?.hasParcelizeAnnotation == true - || getSuperInterfaces().any { DescriptorUtils.isSealedClass(it) && it.hasParcelizeAnnotation } +fun ClassDescriptor.isParcelize(parcelizeAnnotations: List): Boolean = + hasParcelizeAnnotation(parcelizeAnnotations) + || getSuperClassNotAny()?.takeIf(DescriptorUtils::isSealedClass)?.hasParcelizeAnnotation(parcelizeAnnotations) == true + || getSuperInterfaces().any { DescriptorUtils.isSealedClass(it) && it.hasParcelizeAnnotation(parcelizeAnnotations) } val KotlinType.isParceler: Boolean get() = constructor.declarationDescriptor?.fqNameSafe == PARCELER_FQN diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/serializers/ParcelSerializer.kt b/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/serializers/ParcelSerializer.kt index 5fa84509fe5..07630876444 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/serializers/ParcelSerializer.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/serializers/ParcelSerializer.kt @@ -38,6 +38,7 @@ interface ParcelSerializer { fun readValue(v: InstructionAdapter) data class ParcelSerializerContext( + val parcelizeAnnotations: List, val typeMapper: KotlinTypeMapper, val containerClassType: Type, val typeParcelers: List, @@ -344,7 +345,7 @@ interface ParcelSerializer { val creatorAsmType = when { creatorVar != null -> typeMapper.mapTypeSafe(creatorVar.type, forceBoxed = true) - clazz.isParcelize -> Type.getObjectType(asmType.internalName + "\$Creator") + clazz.isParcelize(context.parcelizeAnnotations) -> Type.getObjectType(asmType.internalName + "\$Creator") else -> null } diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/serializers/ParcelizeExtensionBase.kt b/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/serializers/ParcelizeExtensionBase.kt index 9837e8a0d98..d0333e3548e 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/serializers/ParcelizeExtensionBase.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k1/src/org/jetbrains/kotlin/parcelize/serializers/ParcelizeExtensionBase.kt @@ -36,7 +36,8 @@ interface ParcelizeExtensionBase { .isNotEmpty() } - val ClassDescriptor.isParcelizeClassDescriptor get() = kind in ALLOWED_CLASS_KINDS && isParcelize + fun ClassDescriptor.isParcelizeClassDescriptor(parcelizeAnnotations: List) + = kind in ALLOWED_CLASS_KINDS && isParcelize(parcelizeAnnotations) fun ClassDescriptor.hasSyntheticDescribeContents() = hasParcelizeSyntheticMethod(DESCRIBE_CONTENTS) diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeCheckersExtension.kt b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeCheckersExtension.kt index 92411472345..ec7cd110f66 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeCheckersExtension.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeCheckersExtension.kt @@ -10,25 +10,29 @@ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.* import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirAnnotationCallChecker import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.parcelize.fir.diagnostics.* -class FirParcelizeCheckersExtension(session: FirSession) : FirAdditionalCheckersExtension(session) { +class FirParcelizeCheckersExtension( + session: FirSession, + val parcelizeAnnotations: List +) : FirAdditionalCheckersExtension(session) { override val expressionCheckers: ExpressionCheckers = object : ExpressionCheckers() { override val annotationCallCheckers: Set - get() = setOf(FirParcelizeAnnotationChecker) + get() = setOf(FirParcelizeAnnotationChecker(parcelizeAnnotations)) } override val declarationCheckers: DeclarationCheckers = object : DeclarationCheckers() { override val classCheckers: Set - get() = setOf(FirParcelizeClassChecker) + get() = setOf(FirParcelizeClassChecker(parcelizeAnnotations)) override val propertyCheckers: Set - get() = setOf(FirParcelizePropertyChecker) + get() = setOf(FirParcelizePropertyChecker(parcelizeAnnotations)) override val simpleFunctionCheckers: Set - get() = setOf(FirParcelizeFunctionChecker) + get() = setOf(FirParcelizeFunctionChecker(parcelizeAnnotations)) override val constructorCheckers: Set - get() = setOf(FirParcelizeConstructorChecker) + get() = setOf(FirParcelizeConstructorChecker(parcelizeAnnotations)) } } 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 4a4e1c28efb..2a04198c9b6 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 @@ -23,25 +23,28 @@ import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.parcelize.ParcelizeNames.DESCRIBE_CONTENTS_NAME import org.jetbrains.kotlin.parcelize.ParcelizeNames.DEST_NAME import org.jetbrains.kotlin.parcelize.ParcelizeNames.FLAGS_NAME -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) { +class FirParcelizeDeclarationGenerator( + session: FirSession, + private val annotations: List +) : FirDeclarationGenerationExtension(session) { companion object { - private val PREDICATE = LookupPredicate.create { annotated(PARCELIZE_FQN, OLD_PARCELIZE_FQN) } private val parcelizeMethodsNames = setOf(DESCRIBE_CONTENTS_NAME, WRITE_TO_PARCEL_NAME) } + private val predicate = LookupPredicate.create { annotated(annotations) } + private val matchedClasses by lazy { - session.predicateBasedProvider.getSymbolsByPredicate(PREDICATE) + session.predicateBasedProvider.getSymbolsByPredicate(predicate) .filterIsInstance() } @@ -116,6 +119,6 @@ class FirParcelizeDeclarationGenerator(session: FirSession) : FirDeclarationGene get() = ParcelizePluginKey override fun FirDeclarationPredicateRegistrar.registerPredicates() { - register(PREDICATE) + register(predicate) } } diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeExtensionRegistrar.kt b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeExtensionRegistrar.kt index 10f46e3360f..13ad97a837f 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeExtensionRegistrar.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeExtensionRegistrar.kt @@ -6,10 +6,12 @@ package org.jetbrains.kotlin.parcelize.fir import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName -class FirParcelizeExtensionRegistrar : FirExtensionRegistrar() { +class FirParcelizeExtensionRegistrar(val parcelizeAnnotationFqNames: List) : FirExtensionRegistrar() { override fun ExtensionRegistrarContext.configurePlugin() { - +::FirParcelizeDeclarationGenerator - +::FirParcelizeCheckersExtension + +::FirParcelizeDeclarationGenerator.bind(parcelizeAnnotationFqNames) + +::FirParcelizeCheckersExtension.bind(parcelizeAnnotationFqNames.map { ClassId.topLevel(it) }) } } diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt index 5634618a1f4..42caecc3a6f 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt @@ -23,13 +23,13 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.parcelize.ParcelizeNames import org.jetbrains.kotlin.parcelize.ParcelizeNames.DEPRECATED_RUNTIME_PACKAGE import org.jetbrains.kotlin.parcelize.ParcelizeNames.IGNORED_ON_PARCEL_CLASS_IDS -import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELIZE_CLASS_CLASS_IDS import org.jetbrains.kotlin.parcelize.ParcelizeNames.RAW_VALUE_ANNOTATION_CLASS_IDS import org.jetbrains.kotlin.parcelize.ParcelizeNames.TYPE_PARCELER_CLASS_IDS import org.jetbrains.kotlin.parcelize.ParcelizeNames.WRITE_WITH_CLASS_IDS // TODO: extract common checker for expect interfaces -object FirParcelizeAnnotationChecker : FirAnnotationCallChecker(MppCheckerKind.Platform) { +class FirParcelizeAnnotationChecker(private val parcelizeAnnotationClassIds: List) : + FirAnnotationCallChecker(MppCheckerKind.Platform) { override fun check(expression: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) { val annotationType = expression.annotationTypeRef.coneType.fullyExpandedType(context.session) as? ConeClassLikeType ?: return val resolvedAnnotationSymbol = annotationType.lookupTag.toFirRegularClassSymbol(context.session) ?: return @@ -47,7 +47,7 @@ object FirParcelizeAnnotationChecker : FirAnnotationCallChecker(MppCheckerKind.P in IGNORED_ON_PARCEL_CLASS_IDS -> { checkDeprecatedAnnotations(expression, annotationClassId, context, reporter, isForbidden = false) } - in PARCELIZE_CLASS_CLASS_IDS, in RAW_VALUE_ANNOTATION_CLASS_IDS -> { + in parcelizeAnnotationClassIds, in RAW_VALUE_ANNOTATION_CLASS_IDS -> { checkDeprecatedAnnotations(expression, annotationClassId, context, reporter, isForbidden = false) } } @@ -148,7 +148,7 @@ object FirParcelizeAnnotationChecker : FirAnnotationCallChecker(MppCheckerKind.P private fun checkIfTheContainingClassIsParcelize(annotationCall: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) { val enclosingClass = context.findClosestClassOrObject() ?: return - if (!enclosingClass.symbol.isParcelize(context.session)) { + if (!enclosingClass.symbol.isParcelize(context.session, parcelizeAnnotationClassIds)) { val reportElement = annotationCall.calleeReference.source ?: annotationCall.source reporter.reportOn(reportElement, KtErrorsParcelize.CLASS_SHOULD_BE_PARCELIZE, enclosingClass.symbol, context) } 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 04a39846f6e..c935f249881 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 @@ -21,16 +21,16 @@ import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl +import org.jetbrains.kotlin.name.ClassId 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 -object FirParcelizeClassChecker : FirClassChecker(MppCheckerKind.Common) { +class FirParcelizeClassChecker(private val parcelizeAnnotations: List) : FirClassChecker(MppCheckerKind.Platform) { override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) { checkParcelableClass(declaration, context, reporter) checkParcelerClass(declaration, context, reporter) @@ -38,7 +38,7 @@ object FirParcelizeClassChecker : FirClassChecker(MppCheckerKind.Common) { private fun checkParcelableClass(klass: FirClass, context: CheckerContext, reporter: DiagnosticReporter) { val symbol = klass.symbol - if (!symbol.isParcelize(context.session)) return + if (!symbol.isParcelize(context.session, parcelizeAnnotations)) return val source = klass.source ?: return val classKind = klass.classKind @@ -110,14 +110,14 @@ object FirParcelizeClassChecker : FirClassChecker(MppCheckerKind.Common) { } @OptIn(ExperimentalContracts::class) -fun FirClassSymbol<*>?.isParcelize(session: FirSession): Boolean { +fun FirClassSymbol<*>?.isParcelize(session: FirSession, parcelizeAnnotations: List): Boolean { contract { returns(true) implies (this@isParcelize != null) } if (this == null) return false return checkParcelizeClassSymbols(this, session) { symbol -> - symbol.annotations.any { it.toAnnotationClassId(session) in PARCELIZE_CLASS_CLASS_IDS } + symbol.annotations.any { it.toAnnotationClassId(session) in parcelizeAnnotations } } } diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeConstructorChecker.kt b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeConstructorChecker.kt index 5af09353274..7bf42e9135a 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeConstructorChecker.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeConstructorChecker.kt @@ -16,16 +16,20 @@ import org.jetbrains.kotlin.fir.correspondingProperty import org.jetbrains.kotlin.fir.declarations.FirConstructor import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.toAnnotationClassId +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.parcelize.ParcelizeNames -object FirParcelizeConstructorChecker : FirConstructorChecker(MppCheckerKind.Common) { +class FirParcelizeConstructorChecker(private val parcelizeAnnotations: List) : FirConstructorChecker(MppCheckerKind.Platform) { override fun check(declaration: FirConstructor, context: CheckerContext, reporter: DiagnosticReporter) { if (!declaration.isPrimary) return val source = declaration.source ?: return if (source.kind == KtFakeSourceElementKind.ImplicitConstructor) return val containingClass = context.containingDeclarations.last() as? FirRegularClass ?: return val containingClassSymbol = containingClass.symbol - if (!containingClassSymbol.isParcelize(context.session) || containingClass.hasCustomParceler(context.session)) return + if (!containingClassSymbol.isParcelize(context.session, parcelizeAnnotations) + || containingClass.hasCustomParceler(context.session)) { + return + } if (declaration.valueParameters.isEmpty()) { reporter.reportOn(containingClass.source, KtErrorsParcelize.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY, context) diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeFunctionChecker.kt b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeFunctionChecker.kt index 082e841c631..32751dbe001 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeFunctionChecker.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeFunctionChecker.kt @@ -17,11 +17,12 @@ import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.isInt import org.jetbrains.kotlin.fir.types.isUnit import org.jetbrains.kotlin.fir.types.toRegularClassSymbol +import org.jetbrains.kotlin.name.ClassId -object FirParcelizeFunctionChecker : FirSimpleFunctionChecker(MppCheckerKind.Common) { +class FirParcelizeFunctionChecker(private val parcelizeAnnotations: List) : FirSimpleFunctionChecker(MppCheckerKind.Platform) { override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) { val containingClassSymbol = declaration.dispatchReceiverType?.toRegularClassSymbol(context.session) - if (!containingClassSymbol.isParcelize(context.session)) return + if (!containingClassSymbol.isParcelize(context.session, parcelizeAnnotations)) return if (declaration.origin != FirDeclarationOrigin.Source) return if (declaration.isWriteToParcel() && declaration.isOverride) { reporter.reportOn(declaration.source, KtErrorsParcelize.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED, context) diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizePropertyChecker.kt b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizePropertyChecker.kt index 9c68734b0b2..6d6d6985063 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizePropertyChecker.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizePropertyChecker.kt @@ -26,18 +26,19 @@ import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.parcelize.BuiltinParcelableTypes import org.jetbrains.kotlin.parcelize.ParcelizeNames import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATOR_NAME import org.jetbrains.kotlin.parcelize.ParcelizeNames.IGNORED_ON_PARCEL_CLASS_IDS import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELER_ID -object FirParcelizePropertyChecker : FirPropertyChecker(MppCheckerKind.Common) { +class FirParcelizePropertyChecker(private val parcelizeAnnotations: List) : FirPropertyChecker(MppCheckerKind.Platform) { override fun check(declaration: FirProperty, context: CheckerContext, reporter: DiagnosticReporter) { val session = context.session val containingClassSymbol = declaration.dispatchReceiverType?.toRegularClassSymbol(session) ?: return - if (containingClassSymbol.isParcelize(session)) { + if (containingClassSymbol.isParcelize(session, parcelizeAnnotations)) { val fromPrimaryConstructor = declaration.fromPrimaryConstructor ?: false if ( !fromPrimaryConstructor && @@ -54,7 +55,7 @@ object FirParcelizePropertyChecker : FirPropertyChecker(MppCheckerKind.Common) { if (declaration.name == CREATOR_NAME && containingClassSymbol.isCompanion && declaration.hasJvmFieldAnnotation(session)) { val outerClass = context.containingDeclarations.asReversed().getOrNull(1) as? FirRegularClass - if (outerClass != null && outerClass.symbol.isParcelize(session)) { + if (outerClass != null && outerClass.symbol.isParcelize(session, parcelizeAnnotations)) { reporter.reportOn(declaration.source, KtErrorsParcelize.CREATOR_DEFINITION_IS_NOT_ALLOWED, context) } } diff --git a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/services/ParcelizeEnvironmentConfigurator.kt b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/services/ParcelizeEnvironmentConfigurator.kt index ee39cacb77a..f064b768d80 100644 --- a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/services/ParcelizeEnvironmentConfigurator.kt +++ b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/services/ParcelizeEnvironmentConfigurator.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.parcelize.ParcelizeComponentRegistrar +import org.jetbrains.kotlin.parcelize.ParcelizeConfigurationKeys import org.jetbrains.kotlin.parcelize.kotlinxImmutable import org.jetbrains.kotlin.test.model.FrontendKinds import org.jetbrains.kotlin.test.model.TestModule @@ -47,6 +48,11 @@ class ParcelizeEnvironmentConfigurator(testServices: TestServices) : Environment module: TestModule, configuration: CompilerConfiguration ) { - ParcelizeComponentRegistrar.registerParcelizeComponents(this, useFir = module.frontendKind == FrontendKinds.FIR) + val additionalAnnotation = configuration.get(ParcelizeConfigurationKeys.ADDITIONAL_ANNOTATION) + ParcelizeComponentRegistrar.registerParcelizeComponents( + this, + additionalAnnotation, + useFir = module.frontendKind == FrontendKinds.FIR + ) } }