[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.
This commit is contained in:
+4
-4
@@ -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<FqName>) : 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))
|
||||
}
|
||||
|
||||
+5
-5
@@ -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<FqName>) : 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
|
||||
}
|
||||
|
||||
|
||||
+10
-11
@@ -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<FqName>) : 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<Name> {
|
||||
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<FqName>): 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<FqName>): 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
|
||||
|
||||
+2
-1
@@ -38,6 +38,7 @@ interface ParcelSerializer {
|
||||
fun readValue(v: InstructionAdapter)
|
||||
|
||||
data class ParcelSerializerContext(
|
||||
val parcelizeAnnotations: List<FqName>,
|
||||
val typeMapper: KotlinTypeMapper,
|
||||
val containerClassType: Type,
|
||||
val typeParcelers: List<TypeParcelerMapping>,
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -36,7 +36,8 @@ interface ParcelizeExtensionBase {
|
||||
.isNotEmpty()
|
||||
}
|
||||
|
||||
val ClassDescriptor.isParcelizeClassDescriptor get() = kind in ALLOWED_CLASS_KINDS && isParcelize
|
||||
fun ClassDescriptor.isParcelizeClassDescriptor(parcelizeAnnotations: List<FqName>)
|
||||
= kind in ALLOWED_CLASS_KINDS && isParcelize(parcelizeAnnotations)
|
||||
|
||||
fun ClassDescriptor.hasSyntheticDescribeContents() = hasParcelizeSyntheticMethod(DESCRIBE_CONTENTS)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user