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 + ) } }