From 8cdddbfd9d53fda90e116fcbe2298ff4d7da4b66 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Tue, 26 Oct 2021 16:04:14 +0300 Subject: [PATCH] [FIR] Implement checkers for FIR parcelize plugin There is one of checks left unimplemented (FirParcelizePropertyChecker.checkParcelableClassProperty) because it requires huge commonization of detecting which type can be serialized and which not, which is not prioritized job for now --- .../kotlin/fir/types/ConeTypeUtils.kt | 1 + .../LightTreePositioningStrategies.kt | 56 ++++-- .../diagnostics/PositioningStrategies.kt | 14 ++ .../SourceElementPositioningStrategies.kt | 10 + .../kotlin/generators/tests/GenerateTests.kt | 8 +- .../parcelize-compiler/build.gradle.kts | 1 + .../parcelize/ParcelizeResolveExtension.kt | 56 +++--- .../fir/FirParcelizeCheckersExtension.kt | 38 ++++ .../fir/FirParcelizeExtensionRegistrar.kt | 1 + .../FirParcelizeAnnotationChecker.kt | 64 +++++++ .../diagnostics/FirParcelizeClassChecker.kt | 137 +++++++++++++ .../FirParcelizeConstructorChecker.kt | 37 ++++ .../FirParcelizeFunctionChecker.kt | 36 ++++ .../FirParcelizePropertyChecker.kt | 100 ++++++++++ .../KtDefaultErrorMessagesParcelize.kt | 180 ++++++++++++++++++ .../fir/diagnostics/KtErrorsParcelize.kt | 53 ++++++ .../testData/diagnostics/constructors.kt | 1 + .../testData/diagnostics/customCreator.kt | 1 + .../diagnostics/customParcelers.fir.kt | 45 +++++ .../diagnostics/customWriteToParcel.kt | 1 + .../testData/diagnostics/delegate.kt | 1 + .../diagnostics/deprecatedAnnotations.fir.kt | 46 +++++ .../diagnostics/emptyPrimaryConstructor.kt | 1 + .../testData/diagnostics/kt20062.fir.kt | 20 ++ .../testData/diagnostics/modality.fir.kt | 31 +++ .../testData/diagnostics/notMagicParcel.kt | 1 + .../testData/diagnostics/properties.kt | 1 + .../testData/diagnostics/simple.kt | 1 + .../diagnostics/unsupportedType.fir.kt | 17 ++ .../diagnostics/withoutParcelableSupertype.kt | 1 + .../diagnostics/wrongAnnotationTarget.kt | 1 + .../FirParcelizeDiagnosticTestGenerated.java | 115 +++++++++++ .../ParcelizeDiagnosticTestGenerated.java | 5 +- .../AbstractFirParcelizeDiagnosticTest.kt | 35 ++++ .../test/runners/AbstractParcelizeBoxTest.kt | 16 +- .../AbstractParcelizeDiagnosticTest.kt | 3 + .../FirFacadeWithParcelizeExtension.kt | 16 ++ 37 files changed, 1089 insertions(+), 62 deletions(-) create mode 100644 plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeCheckersExtension.kt create mode 100644 plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt create mode 100644 plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeClassChecker.kt create mode 100644 plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeConstructorChecker.kt create mode 100644 plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeFunctionChecker.kt create mode 100644 plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizePropertyChecker.kt create mode 100644 plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/KtDefaultErrorMessagesParcelize.kt create mode 100644 plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/KtErrorsParcelize.kt create mode 100644 plugins/parcelize/parcelize-compiler/testData/diagnostics/customParcelers.fir.kt create mode 100644 plugins/parcelize/parcelize-compiler/testData/diagnostics/deprecatedAnnotations.fir.kt create mode 100644 plugins/parcelize/parcelize-compiler/testData/diagnostics/kt20062.fir.kt create mode 100644 plugins/parcelize/parcelize-compiler/testData/diagnostics/modality.fir.kt create mode 100644 plugins/parcelize/parcelize-compiler/testData/diagnostics/unsupportedType.fir.kt create mode 100644 plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/FirParcelizeDiagnosticTestGenerated.java create mode 100644 plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractFirParcelizeDiagnosticTest.kt create mode 100644 plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/services/FirFacadeWithParcelizeExtension.kt diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt index 26cc2714169..3a0cf29209c 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypeUtils.kt @@ -90,6 +90,7 @@ val ConeKotlinType.isNullableBoolean: Boolean get() = isBuiltinType(StandardClas val ConeKotlinType.isBooleanOrNullableBoolean: Boolean get() = isAnyOfBuiltinType(setOf(StandardClassIds.Boolean)) val ConeKotlinType.isEnum: Boolean get() = isBuiltinType(StandardClassIds.Enum, false) val ConeKotlinType.isString: Boolean get() = isBuiltinType(StandardClassIds.String, false) +val ConeKotlinType.isInt: Boolean get() = isBuiltinType(StandardClassIds.Int, false) val ConeKotlinType.isPrimitiveOrNullablePrimitive: Boolean get() = isAnyOfBuiltinType(StandardClassIds.primitiveTypes) val ConeKotlinType.isPrimitive: Boolean get() = isPrimitiveOrNullablePrimitive && nullability == ConeNullability.NOT_NULL val ConeKotlinType.isArrayType: Boolean diff --git a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt index 100dfe4800b..9067d3da625 100644 --- a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt @@ -420,24 +420,9 @@ object LightTreePositioningStrategies { val TAILREC_MODIFIER: LightTreePositioningStrategy = ModifierSetBasedLightTreePositioningStrategy(TokenSet.create(KtTokens.TAILREC_KEYWORD)) - val FIELD_KEYWORD: LightTreePositioningStrategy = object : LightTreePositioningStrategy() { - override fun mark( - node: LighterASTNode, - startOffset: Int, - endOffset: Int, - tree: FlyweightCapableTreeStructure - ): List { - val fieldKeyword = tree.fieldKeyword(node) - if (fieldKeyword != null) { - return markElement(fieldKeyword, startOffset, endOffset, tree, node) - } - return DEFAULT.mark(node, startOffset, endOffset, tree) - } + val OBJECT_KEYWORD: LightTreePositioningStrategy = keywordStrategy { objectKeyword(it) } - override fun isValid(node: LighterASTNode, tree: FlyweightCapableTreeStructure): Boolean { - return tree.fieldKeyword(node) != null - } - } + val FIELD_KEYWORD: LightTreePositioningStrategy = keywordStrategy { fieldKeyword(it) } val PROPERTY_DELEGATE: LightTreePositioningStrategy = object : LightTreePositioningStrategy() { override fun mark( @@ -1053,6 +1038,22 @@ object LightTreePositioningStrategies { KtTokens.SEALED_KEYWORD ) ) + + val DELEGATED_SUPERTYPE_BY_KEYWORD: LightTreePositioningStrategy = object : LightTreePositioningStrategy() { + override fun mark( + node: LighterASTNode, + startOffset: Int, + endOffset: Int, + tree: FlyweightCapableTreeStructure + ): List { + val parent = tree.getParent(node) + if (parent == null || parent.tokenType != KtNodeTypes.DELEGATED_SUPER_TYPE_ENTRY) { + return super.mark(node, startOffset, endOffset, tree) + } + val byKeyword = parent.getChildren(tree).firstOrNull { it.tokenType == KtTokens.BY_KEYWORD } ?: node + return markElement(byKeyword, startOffset, endOffset, tree, node) + } + } } fun KtSourceElement.hasValOrVar(): Boolean = @@ -1235,6 +1236,27 @@ private fun FlyweightCapableTreeStructure.receiverTypeReference( } } +private fun keywordStrategy( + keywordExtractor: FlyweightCapableTreeStructure.(LighterASTNode) -> LighterASTNode? +): LightTreePositioningStrategy = object : LightTreePositioningStrategy() { + override fun mark( + node: LighterASTNode, + startOffset: Int, + endOffset: Int, + tree: FlyweightCapableTreeStructure + ): List { + val fieldKeyword = tree.keywordExtractor(node) + if (fieldKeyword != null) { + return markElement(fieldKeyword, startOffset, endOffset, tree, node) + } + return LightTreePositioningStrategies.DEFAULT.mark(node, startOffset, endOffset, tree) + } + + override fun isValid(node: LighterASTNode, tree: FlyweightCapableTreeStructure): Boolean { + return tree.keywordExtractor(node) != null + } +} + private fun FlyweightCapableTreeStructure.defaultValue(node: LighterASTNode): LighterASTNode? { val childrenRef = Ref>() getChildren(node, childrenRef) diff --git a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index 41e35a51496..267a6061a4e 100644 --- a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -295,6 +295,13 @@ object PositioningStrategies { @JvmField val TAILREC_MODIFIER: PositioningStrategy = modifierSetPosition(KtTokens.TAILREC_KEYWORD) + @JvmField + val OBJECT_KEYWORD: PositioningStrategy = object : PositioningStrategy() { + override fun mark(element: KtObjectDeclaration): List { + return markElement(element.getObjectKeyword() ?: element) + } + } + @JvmField val FIELD_KEYWORD: PositioningStrategy = object : DeclarationHeader() { override fun mark(element: KtBackingField): List { @@ -893,6 +900,13 @@ object PositioningStrategies { val NON_FINAL_MODIFIER_OR_NAME: PositioningStrategy = ModifierSetBasedPositioningStrategy(TokenSet.create(KtTokens.ABSTRACT_KEYWORD, KtTokens.OPEN_KEYWORD, KtTokens.SEALED_KEYWORD)) + val DELEGATED_SUPERTYPE_BY_KEYWORD: PositioningStrategy = object : PositioningStrategy() { + override fun mark(element: KtTypeReference): List { + val parent = element.parent as? KtDelegatedSuperTypeEntry ?: return super.mark(element) + return markElement(parent.byKeywordNode.psi ?: element) + } + } + /** * @param locateReferencedName whether to remove any nested parentheses while locating the reference element. This is useful for * diagnostics on super and unresolved references. For example, with the following, only the part inside the parentheses should be diff --git a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/SourceElementPositioningStrategies.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/SourceElementPositioningStrategies.kt index 9047737eede..435cf8a8c82 100644 --- a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/SourceElementPositioningStrategies.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/SourceElementPositioningStrategies.kt @@ -123,6 +123,11 @@ object SourceElementPositioningStrategies { PositioningStrategies.DATA_MODIFIER ) + val OBJECT_KEYWORD = SourceElementPositioningStrategy( + LightTreePositioningStrategies.OBJECT_KEYWORD, + PositioningStrategies.OBJECT_KEYWORD + ) + val OPERATOR = SourceElementPositioningStrategy( LightTreePositioningStrategies.OPERATOR, PositioningStrategies.OPERATOR @@ -344,4 +349,9 @@ object SourceElementPositioningStrategies { LightTreePositioningStrategies.DECLARATION_START_TO_NAME, PositioningStrategies.DECLARATION_START_TO_NAME ) + + val DELEGATED_SUPERTYPE_BY_KEYWORD = SourceElementPositioningStrategy( + LightTreePositioningStrategies.DELEGATED_SUPERTYPE_BY_KEYWORD, + PositioningStrategies.DELEGATED_SUPERTYPE_BY_KEYWORD + ) } diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 14604060c0c..c563a45e915 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -350,6 +350,8 @@ fun main(args: Array) { } generateTestGroupSuiteWithJUnit5 { + val excludedFirTestdataPattern = "^(.+)\\.fir\\.kts?\$" + testGroup("plugins/parcelize/parcelize-compiler/tests-gen", "plugins/parcelize/parcelize-compiler/testData") { testClass { model("box") @@ -372,7 +374,11 @@ fun main(args: Array) { } testClass { - model("diagnostics") + model("diagnostics", excludedPattern = excludedFirTestdataPattern) + } + + testClass { + model("diagnostics", excludedPattern = excludedFirTestdataPattern) } } diff --git a/plugins/parcelize/parcelize-compiler/build.gradle.kts b/plugins/parcelize/parcelize-compiler/build.gradle.kts index 70d6896415d..b2178998927 100644 --- a/plugins/parcelize/parcelize-compiler/build.gradle.kts +++ b/plugins/parcelize/parcelize-compiler/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { compileOnly(project(":compiler:fir:tree")) compileOnly(project(":compiler:fir:resolve")) compileOnly(project(":compiler:fir:checkers")) + compileOnly(project(":compiler:fir:checkers:checkers.jvm")) compileOnly(project(":compiler:fir:fir2ir")) compileOnly(project(":compiler:ir.backend.common")) compileOnly(project(":compiler:ir.tree.impl")) diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeResolveExtension.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeResolveExtension.kt index b4d19c55657..5cd542be435 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeResolveExtension.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeResolveExtension.kt @@ -31,11 +31,7 @@ import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind. import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny -import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces -import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.descriptorUtil.* import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension import org.jetbrains.kotlin.resolve.source.PsiSourceElement import org.jetbrains.kotlin.types.ErrorUtils @@ -174,33 +170,39 @@ interface ParcelizeSyntheticComponent { } } -val TYPE_PARCELER_FQ_NAMES = listOf( - FqName("kotlinx.parcelize.TypeParceler"), - FqName("kotlinx.android.parcel.TypeParceler") +val PACKAGES_FQ_NAMES = listOf( + FqName("kotlinx.parcelize"), + FqName("kotlinx.android.parcel") ) -val WRITE_WITH_FQ_NAMES = listOf( - FqName("kotlinx.parcelize.WriteWith"), - FqName("kotlinx.android.parcel.WriteWith") -) +private fun createClassIds(name: String): List { + return PACKAGES_FQ_NAMES.map { ClassId(it, Name.identifier(name)) } +} -val IGNORED_ON_PARCEL_FQ_NAMES = listOf( - FqName("kotlinx.parcelize.IgnoredOnParcel"), - FqName("kotlinx.android.parcel.IgnoredOnParcel") -) +private fun List.fqNames(): List { + return map { it.asSingleFqName() } +} -val PARCELIZE_CLASS_FQ_NAMES: List = listOf( - FqName("kotlinx.parcelize.Parcelize"), - FqName("kotlinx.android.parcel.Parcelize") -) +val TYPE_PARCELER_CLASS_IDS = createClassIds("TypeParceler") +val TYPE_PARCELER_FQ_NAMES = TYPE_PARCELER_CLASS_IDS.fqNames() -val RAW_VALUE_ANNOTATION_FQ_NAMES = listOf( - FqName("kotlinx.parcelize.RawValue"), - FqName("kotlinx.android.parcel.RawValue") -) +val WRITE_WITH_CLASS_IDS = createClassIds("WriteWith") +val WRITE_WITH_FQ_NAMES = WRITE_WITH_CLASS_IDS.fqNames() -internal val PARCELER_FQNAME = FqName("kotlinx.parcelize.Parceler") -internal val OLD_PARCELER_FQNAME = FqName("kotlinx.android.parcel.Parceler") +val IGNORED_ON_PARCEL_CLASS_IDS = createClassIds("IgnoredOnParcel") +val IGNORED_ON_PARCEL_FQ_NAMES = IGNORED_ON_PARCEL_CLASS_IDS.fqNames() + +val PARCELIZE_CLASS_CLASS_IDS = createClassIds("Parcelize") +val PARCELIZE_CLASS_FQ_NAMES: List = PARCELIZE_CLASS_CLASS_IDS.fqNames() + +val RAW_VALUE_ANNOTATION_CLASS_IDS = createClassIds("RawValue") +val RAW_VALUE_ANNOTATION_FQ_NAMES = RAW_VALUE_ANNOTATION_CLASS_IDS.fqNames() + +internal val PARCELER_CLASS_ID = ClassId(FqName("kotlinx.parcelize"), Name.identifier("Parceler")) +internal val PARCELER_FQNAME = PARCELER_CLASS_ID.asSingleFqName() + +internal val OLD_PARCELER_CLASS_ID = ClassId(FqName("kotlinx.android.parcel"), Name.identifier("Parceler")) +internal val OLD_PARCELER_FQNAME = OLD_PARCELER_CLASS_ID.asSingleFqName() val ClassDescriptor.hasParcelizeAnnotation: Boolean get() = PARCELIZE_CLASS_FQ_NAMES.any(annotations::hasAnnotation) @@ -232,4 +234,4 @@ fun Annotated.hasAnyAnnotation(fqNames: List): Boolean { } return false -} \ No newline at end of file +} diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeCheckersExtension.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeCheckersExtension.kt new file mode 100644 index 00000000000..f16cdaa6141 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeCheckersExtension.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2021 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.fir + +import org.jetbrains.kotlin.fir.FirSession +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.fir.declarations.FirPluginKey +import org.jetbrains.kotlin.parcelize.fir.diagnostics.* + +class FirParcelizeCheckersExtension(session: FirSession) : FirAdditionalCheckersExtension(session) { + override val key: FirPluginKey + get() = FirParcelizePluginKey + + override val expressionCheckers: ExpressionCheckers = object : ExpressionCheckers() { + override val annotationCallCheckers: Set + get() = setOf(FirParcelizeAnnotationChecker) + } + + override val declarationCheckers: DeclarationCheckers = object : DeclarationCheckers() { + override val classCheckers: Set + get() = setOf(FirParcelizeClassChecker) + + override val propertyCheckers: Set + get() = setOf(FirParcelizePropertyChecker) + + override val simpleFunctionCheckers: Set + get() = setOf(FirParcelizeFunctionChecker) + + override val constructorCheckers: Set + get() = setOf(FirParcelizeConstructorChecker) + } +} diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeExtensionRegistrar.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeExtensionRegistrar.kt index 58cff107abc..10f46e3360f 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeExtensionRegistrar.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeExtensionRegistrar.kt @@ -10,5 +10,6 @@ import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar class FirParcelizeExtensionRegistrar : FirExtensionRegistrar() { override fun ExtensionRegistrarContext.configurePlugin() { +::FirParcelizeDeclarationGenerator + +::FirParcelizeCheckersExtension } } diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt new file mode 100644 index 00000000000..6e10715194c --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2010-2021 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.fir.diagnostics + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirAnnotationCallChecker +import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall +import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol +import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.parcelize.* + +object FirParcelizeAnnotationChecker : FirAnnotationCallChecker() { + 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 + when (val annotationClassId = resolvedAnnotationSymbol.classId) { + in TYPE_PARCELER_CLASS_IDS -> { + checkTypeParcelerUsage(expression, context, reporter) + checkDeprecatedAnnotations(expression, annotationClassId, context, reporter, isForbidden = true) + } + in WRITE_WITH_CLASS_IDS -> { + checkWriteWithUsage(expression, context, reporter) + checkDeprecatedAnnotations(expression, annotationClassId, context, reporter, isForbidden = true) + } + in IGNORED_ON_PARCEL_CLASS_IDS -> { + checkDeprecatedAnnotations(expression, annotationClassId, context, reporter, isForbidden = false) + } + in PARCELIZE_CLASS_CLASS_IDS, in RAW_VALUE_ANNOTATION_CLASS_IDS -> { + checkDeprecatedAnnotations(expression, annotationClassId, context, reporter, isForbidden = false) + } + } + } + + private fun checkDeprecatedAnnotations( + annotationCall: FirAnnotationCall, + annotationClassId: ClassId, + context: CheckerContext, + reporter: DiagnosticReporter, + isForbidden: Boolean + ) { + if (annotationClassId.packageFqName == ParcelizeAnnotationChecker.DEPRECATED_RUNTIME_PACKAGE) { + val factory = if (isForbidden) KtErrorsParcelize.FORBIDDEN_DEPRECATED_ANNOTATION else KtErrorsParcelize.DEPRECATED_ANNOTATION + reporter.reportOn(annotationCall.source, factory, context) + } + } + + @Suppress("UNUSED_PARAMETER") + private fun checkTypeParcelerUsage(annotationCall: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) { + // TODO: this check checks type arguments of annotation which are not supported in FIR + } + + @Suppress("UNUSED_PARAMETER") + private fun checkWriteWithUsage(annotationCall: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) { + // TODO: this check checks type arguments of annotation which are not supported in FIR + } +} diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeClassChecker.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeClassChecker.kt new file mode 100644 index 00000000000..af4def9ea96 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeClassChecker.kt @@ -0,0 +1,137 @@ +/* + * Copyright 2010-2021 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.fir.diagnostics + +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker +import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol +import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics +import org.jetbrains.kotlin.fir.declarations.FirClass +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.constructors +import org.jetbrains.kotlin.fir.declarations.delegateFieldsMap +import org.jetbrains.kotlin.fir.declarations.utils.* +import org.jetbrains.kotlin.fir.expressions.classId +import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes +import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.types.classId +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl +import org.jetbrains.kotlin.fir.types.isSubtypeOf +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.SpecialNames +import org.jetbrains.kotlin.parcelize.OLD_PARCELER_CLASS_ID +import org.jetbrains.kotlin.parcelize.PARCELIZE_CLASS_CLASS_IDS +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + +object FirParcelizeClassChecker : FirClassChecker() { + private val CREATOR_NAME = Name.identifier("CREATOR") + private val PARCELABLE_CLASS_ID = ClassId(FqName("android.os"), Name.identifier("Parcelable")) + + override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) { + checkParcelableClass(declaration, context, reporter) + checkParcelerClass(declaration, context, reporter) + } + + private fun checkParcelableClass(klass: FirClass, context: CheckerContext, reporter: DiagnosticReporter) { + val symbol = klass.symbol + if (!symbol.isParcelize(context.session)) return + val source = klass.source ?: return + if (klass !is FirRegularClass) { + reporter.reportOn(source, KtErrorsParcelize.PARCELABLE_SHOULD_BE_CLASS, context) + return + } + + val classKind = klass.classKind + if (classKind == ClassKind.ANNOTATION_CLASS || classKind == ClassKind.INTERFACE && !klass.isSealed) { + reporter.reportOn(source, KtErrorsParcelize.PARCELABLE_SHOULD_BE_CLASS, context) + return + } + + klass.companionObjectSymbol?.let { companionSymbol -> + if (companionSymbol.classId.shortClassName == CREATOR_NAME) { + reporter.reportOn(companionSymbol.source, KtErrorsParcelize.CREATOR_DEFINITION_IS_NOT_ALLOWED, context) + } + } + + if (classKind == ClassKind.CLASS && klass.isAbstract) { + reporter.reportOn(source, KtErrorsParcelize.PARCELABLE_SHOULD_BE_INSTANTIABLE, context) + } + + if (klass.isInner) { + reporter.reportOn(source, KtErrorsParcelize.PARCELABLE_CANT_BE_INNER_CLASS, context) + } + + if (klass.isLocal) { + reporter.reportOn(source, KtErrorsParcelize.PARCELABLE_CANT_BE_LOCAL_CLASS, context) + } + + val supertypes = lookupSuperTypes(klass, lookupInterfaces = true, deep = true, context.session, substituteTypes = false) + if (supertypes.none { it.classId == PARCELABLE_CLASS_ID }) { + reporter.reportOn(source, KtErrorsParcelize.NO_PARCELABLE_SUPERTYPE, context) + } + + klass.delegateFieldsMap?.forEach { (index, _) -> + val superTypeRef = klass.superTypeRefs[index] + val superType = superTypeRef.coneType + val parcelableType = ConeClassLikeTypeImpl( + ConeClassLikeLookupTagImpl(PARCELABLE_CLASS_ID), + emptyArray(), + isNullable = false + ) + if (superType.isSubtypeOf(parcelableType, context.session)) { + reporter.reportOn(superTypeRef.source, KtErrorsParcelize.PARCELABLE_DELEGATE_IS_NOT_ALLOWED, context) + } + } + + val constructorSymbols = klass.constructors(context.session) + val primaryConstructorSymbol = constructorSymbols.find { it.isPrimary } + val secondaryConstructorSymbols = constructorSymbols.filterNot { it.isPrimary } + if (primaryConstructorSymbol == null && secondaryConstructorSymbols.isNotEmpty()) { + reporter.reportOn(source, KtErrorsParcelize.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR, context) + } + } + + private fun checkParcelerClass(klass: FirClass, context: CheckerContext, reporter: DiagnosticReporter) { + if (klass !is FirRegularClass || klass.isCompanion) return + for (superTypeRef in klass.superTypeRefs) { + withSuppressedDiagnostics(superTypeRef, context) { + if (superTypeRef.coneType.classId == OLD_PARCELER_CLASS_ID) { + val strategy = if (klass.name == SpecialNames.NO_NAME_PROVIDED) { + SourceElementPositioningStrategies.OBJECT_KEYWORD + } else { + SourceElementPositioningStrategies.NAME_IDENTIFIER + } + reporter.reportOn(klass.source, KtErrorsParcelize.DEPRECATED_PARCELER, context, positioningStrategy = strategy) + } + } + } + } +} + +@OptIn(ExperimentalContracts::class) +fun FirClassSymbol<*>?.isParcelize(session: FirSession): Boolean { + contract { + returns(true) implies (this@isParcelize != null) + } + + if (this == null) return false + if (this.annotations.any { it.classId in PARCELIZE_CLASS_CLASS_IDS }) return true + return resolvedSuperTypeRefs.any { + val symbol = it.type.fullyExpandedType(session).toRegularClassSymbol(session) ?: return@any false + symbol.annotations.any { it.classId in PARCELIZE_CLASS_CLASS_IDS } + } +} diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeConstructorChecker.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeConstructorChecker.kt new file mode 100644 index 00000000000..8db67989d1a --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeConstructorChecker.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2021 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.fir.diagnostics + +import org.jetbrains.kotlin.KtFakeSourceElementKind +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.hasValOrVar +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirConstructorChecker +import org.jetbrains.kotlin.fir.declarations.FirConstructor +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR + +object FirParcelizeConstructorChecker : FirConstructorChecker() { + 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)) return + + if (declaration.valueParameters.isEmpty()) { + reporter.reportOn(containingClass.source, KtErrorsParcelize.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY, context) + } else { + for (valueParameter in declaration.valueParameters) { + if (valueParameter.source?.hasValOrVar() != true) { + reporter.reportOn(valueParameter.source, PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR, context) + } + } + } + } +} diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeFunctionChecker.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeFunctionChecker.kt new file mode 100644 index 00000000000..2c9f307bce6 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeFunctionChecker.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2021 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.fir.diagnostics + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirSimpleFunctionChecker +import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin +import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.declarations.utils.isOverride +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.isInt +import org.jetbrains.kotlin.fir.types.isUnit + +object FirParcelizeFunctionChecker : FirSimpleFunctionChecker() { + override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) { + val containingClassSymbol = declaration.dispatchReceiverType?.toRegularClassSymbol(context.session) + if (!containingClassSymbol.isParcelize(context.session)) 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) + } + } + + private fun FirSimpleFunction.isWriteToParcel(): Boolean { + return typeParameters.isEmpty() && + valueParameters.size == 2 && + valueParameters[1].returnTypeRef.coneType.isInt && + returnTypeRef.coneType.isUnit + } +} diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizePropertyChecker.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizePropertyChecker.kt new file mode 100644 index 00000000000..8a01c1d5166 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizePropertyChecker.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2010-2021 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.fir.diagnostics + +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirPropertyChecker +import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol +import org.jetbrains.kotlin.fir.declarations.FirProperty +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.hasJvmFieldAnnotation +import org.jetbrains.kotlin.fir.declarations.utils.fromPrimaryConstructor +import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField +import org.jetbrains.kotlin.fir.declarations.utils.isCompanion +import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.expressions.classId +import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType +import org.jetbrains.kotlin.fir.types.classId +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.parcelize.IGNORED_ON_PARCEL_CLASS_IDS +import org.jetbrains.kotlin.parcelize.PARCELER_CLASS_ID + +object FirParcelizePropertyChecker : FirPropertyChecker() { + private val CREATOR_NAME = Name.identifier("CREATOR") + + override fun check(declaration: FirProperty, context: CheckerContext, reporter: DiagnosticReporter) { + val containingClassSymbol = declaration.dispatchReceiverType?.toRegularClassSymbol(context.session) ?: return + + if (containingClassSymbol.isParcelize(context.session)) { + val fromPrimaryConstructor = declaration.fromPrimaryConstructor ?: false + if ( + !fromPrimaryConstructor && + (declaration.hasBackingField || declaration.delegate != null) && + !declaration.hasIgnoredOnParcel() + ) { + reporter.reportOn(declaration.source, KtErrorsParcelize.PROPERTY_WONT_BE_SERIALIZED, context) + } + if (fromPrimaryConstructor) { + checkParcelableClassProperty(declaration, containingClassSymbol, context, reporter) + checkIgnoredOnParcelUsage(declaration, context, reporter) + } + } + + if (declaration.name == CREATOR_NAME && containingClassSymbol.isCompanion && declaration.hasJvmFieldAnnotation) { + val outerClass = context.containingDeclarations.asReversed().getOrNull(1) as? FirRegularClass + if (outerClass != null && outerClass.symbol.isParcelize(context.session)) { + reporter.reportOn(declaration.source, KtErrorsParcelize.CREATOR_DEFINITION_IS_NOT_ALLOWED, context) + } + } + } + + private fun checkIgnoredOnParcelUsage(property: FirProperty, context: CheckerContext, reporter: DiagnosticReporter) { + val illegalAnnotation = property.annotations.firstOrNull { it.classId in IGNORED_ON_PARCEL_CLASS_IDS } ?: return + reporter.reportOn(illegalAnnotation.source, KtErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY, context) + } + + @Suppress("UNUSED_PARAMETER") + private fun checkParcelableClassProperty( + property: FirProperty, + containingClassSymbol: FirRegularClassSymbol, + context: CheckerContext, + reporter: DiagnosticReporter + ) { + val type = property.returnTypeRef.coneType + if (type is ConeKotlinErrorType || containingClassSymbol.hasCustomParceler(context.session)) return + /* + * TODO: abstract code from ParcelSerializer or IrParcelSerializerFactory to avoid duplication + * of allowed types checking + */ + } + + private fun FirProperty.hasIgnoredOnParcel(): Boolean { + return annotations.hasIgnoredOnParcel() || (getter?.annotations?.hasIgnoredOnParcel() ?: false) + } + + + private fun List.hasIgnoredOnParcel(): Boolean { + return this.any { + if (it.annotationTypeRef.coneType.classId !in IGNORED_ON_PARCEL_CLASS_IDS) return@any false + val target = it.useSiteTarget + target == null || target == AnnotationUseSiteTarget.PROPERTY || target == AnnotationUseSiteTarget.PROPERTY_GETTER + } + } + + private fun FirRegularClassSymbol.hasCustomParceler(session: FirSession): Boolean { + val companionObjectSymbol = this.companionObjectSymbol ?: return false + return lookupSuperTypes(companionObjectSymbol, lookupInterfaces = true, deep = true, session).any { + it.classId == PARCELER_CLASS_ID + } + } +} diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/KtDefaultErrorMessagesParcelize.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/KtDefaultErrorMessagesParcelize.kt new file mode 100644 index 00000000000..cb2688fc969 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/KtDefaultErrorMessagesParcelize.kt @@ -0,0 +1,180 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.parcelize.fir.diagnostics + +import org.jetbrains.kotlin.diagnostics.KtDiagnostic +import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap +import org.jetbrains.kotlin.diagnostics.KtDiagnosticRenderer +import org.jetbrains.kotlin.diagnostics.rendering.Renderers.RENDER_CLASS_OR_OBJECT +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_TYPE +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.CLASS_SHOULD_BE_PARCELIZE +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.CREATOR_DEFINITION_IS_NOT_ALLOWED +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.DEPRECATED_ANNOTATION +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.DEPRECATED_PARCELER +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.DUPLICATING_TYPE_PARCELERS +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.FORBIDDEN_DEPRECATED_ANNOTATION +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.NO_PARCELABLE_SUPERTYPE +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PARCELABLE_CANT_BE_INNER_CLASS +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PARCELABLE_CANT_BE_LOCAL_CLASS +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PARCELABLE_DELEGATE_IS_NOT_ALLOWED +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PARCELABLE_SHOULD_BE_CLASS +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PARCELABLE_SHOULD_BE_INSTANTIABLE +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PARCELABLE_SHOULD_NOT_BE_ENUM_CLASS +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PARCELABLE_TYPE_NOT_SUPPORTED +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PARCELER_SHOULD_BE_OBJECT +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PARCELER_TYPE_INCOMPATIBLE +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.PROPERTY_WONT_BE_SERIALIZED +import org.jetbrains.kotlin.parcelize.fir.diagnostics.KtErrorsParcelize.REDUNDANT_TYPE_PARCELER + +object KtDefaultErrorMessagesParcelize { + fun getRendererForDiagnostic(diagnostic: KtDiagnostic): KtDiagnosticRenderer { + val factory = diagnostic.factory + return MAP[factory] ?: factory.ktRenderer + } + + val MAP = KtDiagnosticFactoryToRendererMap("Parcelize").also { map -> + map.put( + PARCELABLE_SHOULD_BE_CLASS, + "'Parcelable' should be a class" + ) + + map.put( + PARCELABLE_DELEGATE_IS_NOT_ALLOWED, + "Delegating 'Parcelable' is not allowed" + ) + + map.put( + PARCELABLE_SHOULD_NOT_BE_ENUM_CLASS, + "'Parcelable' should not be a 'enum class'" + ) + + map.put( + PARCELABLE_SHOULD_BE_INSTANTIABLE, + "'Parcelable' should not be an 'abstract' class" + ) + + map.put( + PARCELABLE_CANT_BE_INNER_CLASS, + "'Parcelable' can't be an inner class" + ) + + map.put( + PARCELABLE_CANT_BE_LOCAL_CLASS, + "'Parcelable' can't be a local class" + ) + + map.put( + NO_PARCELABLE_SUPERTYPE, + "No 'Parcelable' supertype" + ) + + map.put( + PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR, + "'Parcelable' should have a primary constructor" + ) + + map.put( + PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY, + "The primary constructor is empty, no data will be serialized to 'Parcel'" + ) + + map.put( + PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR, + "'Parcelable' constructor parameter should be 'val' or 'var'" + ) + + map.put( + PROPERTY_WONT_BE_SERIALIZED, + "Property would not be serialized into a 'Parcel'. Add '@IgnoredOnParcel' annotation to remove the warning" + ) + + map.put( + OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED, + "Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead" + ) + + map.put( + CREATOR_DEFINITION_IS_NOT_ALLOWED, + "'CREATOR' definition is not allowed. Use 'Parceler' companion object instead" + ) + + map.put( + PARCELABLE_TYPE_NOT_SUPPORTED, + "Type is not directly supported by 'Parcelize'. " + + "Annotate the parameter type with '@RawValue' if you want it to be serialized using 'writeValue()'" + ) + + map.put( + PARCELER_SHOULD_BE_OBJECT, + "Parceler should be an object" + ) + + map.put( + PARCELER_TYPE_INCOMPATIBLE, + "Parceler type {0} is incompatible with {1}", + RENDER_TYPE, RENDER_TYPE + ) + + map.put( + DUPLICATING_TYPE_PARCELERS, + "Duplicating ''TypeParceler'' annotations" + ) + + map.put( + REDUNDANT_TYPE_PARCELER, + "This ''TypeParceler'' is already provided for {0}", + RENDER_CLASS_OR_OBJECT + ) + + map.put( + CLASS_SHOULD_BE_PARCELIZE, + "{0} should be annotated with ''@Parcelize''", + RENDER_CLASS_OR_OBJECT + ) + + map.put( + INAPPLICABLE_IGNORED_ON_PARCEL, + "'@IgnoredOnParcel' is only applicable to class properties" + ) + + map.put( + INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY, + "'@IgnoredOnParcel' is inapplicable to properties declared in the primary constructor" + ) + + map.put( + FORBIDDEN_DEPRECATED_ANNOTATION, + "Parceler-related annotations from package 'kotlinx.android.parcel' are forbidden. Change package to 'kotlinx.parcelize'" + ) + + map.put( + DEPRECATED_ANNOTATION, + "Parcelize annotations from package 'kotlinx.android.parcel' are deprecated. Change package to 'kotlinx.parcelize'" + ) + + map.put( + DEPRECATED_PARCELER, + "'kotlinx.android.parcel.Parceler' is deprecated. Use 'kotlinx.parcelize.Parceler' instead" + ) + } +} diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/KtErrorsParcelize.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/KtErrorsParcelize.kt new file mode 100644 index 00000000000..25ba24eb0d2 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/KtErrorsParcelize.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.kotlin.parcelize.fir.diagnostics + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.diagnostics.* +import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies.ABSTRACT_MODIFIER +import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies.DELEGATED_SUPERTYPE_BY_KEYWORD +import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies.INNER_MODIFIER +import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies.NAME_IDENTIFIER +import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies.OVERRIDE_MODIFIER +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.psi.KtClassOrObject + +object KtErrorsParcelize { + val PARCELABLE_SHOULD_BE_CLASS by error0(NAME_IDENTIFIER) + val PARCELABLE_DELEGATE_IS_NOT_ALLOWED by error0(DELEGATED_SUPERTYPE_BY_KEYWORD) + val PARCELABLE_SHOULD_NOT_BE_ENUM_CLASS by error0() + val PARCELABLE_SHOULD_BE_INSTANTIABLE by error0(ABSTRACT_MODIFIER) + val PARCELABLE_CANT_BE_INNER_CLASS by error0(INNER_MODIFIER) + val PARCELABLE_CANT_BE_LOCAL_CLASS by error0(NAME_IDENTIFIER) + val NO_PARCELABLE_SUPERTYPE by error0(NAME_IDENTIFIER) + val PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR by error0(NAME_IDENTIFIER) + val PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY by warning0(NAME_IDENTIFIER) + val PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR by error0(NAME_IDENTIFIER) + val PROPERTY_WONT_BE_SERIALIZED by warning0(NAME_IDENTIFIER) + val OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED by error0(OVERRIDE_MODIFIER) + val CREATOR_DEFINITION_IS_NOT_ALLOWED by error0(NAME_IDENTIFIER) + val PARCELABLE_TYPE_NOT_SUPPORTED by error0() + val PARCELER_SHOULD_BE_OBJECT by error0() + val PARCELER_TYPE_INCOMPATIBLE by error2() + val DUPLICATING_TYPE_PARCELERS by error0() + val REDUNDANT_TYPE_PARCELER by warning1() + val CLASS_SHOULD_BE_PARCELIZE by error1() + val FORBIDDEN_DEPRECATED_ANNOTATION by error0() + val DEPRECATED_ANNOTATION by warning0() + val DEPRECATED_PARCELER by error0() + val INAPPLICABLE_IGNORED_ON_PARCEL by warning0() + val INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY by warning0() +} diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/constructors.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/constructors.kt index ebe0705b210..d86fc86d39f 100644 --- a/plugins/parcelize/parcelize-compiler/testData/diagnostics/constructors.kt +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/constructors.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB package test diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/customCreator.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/customCreator.kt index b5fd8022103..c07e2aae632 100644 --- a/plugins/parcelize/parcelize-compiler/testData/diagnostics/customCreator.kt +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/customCreator.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB package test diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/customParcelers.fir.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/customParcelers.fir.kt new file mode 100644 index 00000000000..2167b5ef666 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/customParcelers.fir.kt @@ -0,0 +1,45 @@ +// WITH_STDLIB +package test + +import kotlinx.parcelize.* +import android.os.* + +object StringParceler : Parceler { + override fun create(parcel: Parcel) = TODO() + override fun String.write(parcel: Parcel, flags: Int) = TODO() +} + +object CharSequenceParceler : Parceler { + override fun create(parcel: Parcel) = TODO() + override fun CharSequence.write(parcel: Parcel, flags: Int) = TODO() +} + +class StringClassParceler : Parceler { + override fun create(parcel: Parcel) = TODO() + override fun String.write(parcel: Parcel, flags: Int) = TODO() +} + +@TypeParceler +class MissingParcelizeAnnotation(val a: @WriteWith String) + +@Parcelize +@TypeParceler +class ShouldBeClass(val a: @WriteWith String) : Parcelable + +@Parcelize +class Test( + val a: @WriteWith Int, + val b: @WriteWith String, + val c: @WriteWith CharSequence, + val d: @WriteWith String, + val e: @WriteWith CharSequence +) : Parcelable + +@Parcelize +@TypeParceler +class Test2(@TypeParceler val a: String) : Parcelable + +@Parcelize +@TypeParceler +@TypeParceler +class Test3(val a: String) : Parcelable diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/customWriteToParcel.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/customWriteToParcel.kt index 519a732362e..a624a0a2ddd 100644 --- a/plugins/parcelize/parcelize-compiler/testData/diagnostics/customWriteToParcel.kt +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/customWriteToParcel.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL package test import kotlinx.parcelize.Parcelize diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/delegate.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/delegate.kt index ad3912dcdaf..9d0c49d878d 100644 --- a/plugins/parcelize/parcelize-compiler/testData/diagnostics/delegate.kt +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/delegate.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL package test import kotlinx.parcelize.Parcelize diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/deprecatedAnnotations.fir.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/deprecatedAnnotations.fir.kt new file mode 100644 index 00000000000..3863793bda5 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/deprecatedAnnotations.fir.kt @@ -0,0 +1,46 @@ +// WITH_STDLIB +package test + +import kotlinx.android.parcel.* +import android.os.Parcel +import android.os.Parcelable + +object Parceler1 : Parceler { + override fun create(parcel: Parcel) = parcel.readInt().toString() + + override fun String.write(parcel: Parcel, flags: Int) { + parcel.writeInt(length) + } +} + +object Parceler2 : Parceler> { + override fun create(parcel: Parcel) = listOf(parcel.readString()!!) + + override fun List.write(parcel: Parcel, flags: Int) { + parcel.writeString(this.joinToString(",")) + } +} + +@Parcelize +@TypeParceler +data class Test( + val a: String, + val b: @WriteWith String, + val c: @WriteWith List<@WriteWith String> +) : Parcelable { + @IgnoredOnParcel + val x by lazy { "foo" } +} + +interface ParcelerForUser: Parceler + +@Parcelize +class User(val name: String) : Parcelable { + private companion object : ParcelerForUser { + override fun User.write(parcel: Parcel, flags: Int) { + parcel.writeString(name) + } + + override fun create(parcel: Parcel) = User(parcel.readString()!!) + } +} diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/emptyPrimaryConstructor.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/emptyPrimaryConstructor.kt index 55f3bca3a12..f5da895a7f2 100644 --- a/plugins/parcelize/parcelize-compiler/testData/diagnostics/emptyPrimaryConstructor.kt +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/emptyPrimaryConstructor.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL package test import kotlinx.parcelize.Parcelize diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/kt20062.fir.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/kt20062.fir.kt new file mode 100644 index 00000000000..939b87f978b --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/kt20062.fir.kt @@ -0,0 +1,20 @@ +package test + +import kotlinx.parcelize.* +import android.os.* + +class Box(val value: String) + +@Parcelize +class Foo(val box: Box): Parcelable { + companion object : Parceler { + override fun create(parcel: Parcel) = Foo(Box(parcel.readString())) + + override fun Foo.write(parcel: Parcel, flags: Int) { + parcel.writeString(box.value) + } + } +} + +@Parcelize +class Foo2(val box: Box): Parcelable diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/modality.fir.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/modality.fir.kt new file mode 100644 index 00000000000..2d8e453a6f2 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/modality.fir.kt @@ -0,0 +1,31 @@ +package test + +import kotlinx.parcelize.Parcelize +import android.os.Parcelable + +@Parcelize +open class Open(val foo: String) : Parcelable + +@Parcelize +class Final(val foo: String) : Parcelable + +@Parcelize +abstract class Abstract(val foo: String) : Parcelable + +@Parcelize +sealed class Sealed(val foo: String) : Parcelable { + class X : Sealed("") +} + +class Outer { + @Parcelize + inner class Inner(val foo: String) : Parcelable +} + +fun foo() { + @Parcelize + object : Parcelable {} + + @Parcelize + class Local {} +} diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/notMagicParcel.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/notMagicParcel.kt index 2c84e641e62..0fe152f4373 100644 --- a/plugins/parcelize/parcelize-compiler/testData/diagnostics/notMagicParcel.kt +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/notMagicParcel.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL package test import kotlinx.parcelize.Parcelize diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/properties.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/properties.kt index cf02ad6ed4f..7accf7d15dc 100644 --- a/plugins/parcelize/parcelize-compiler/testData/diagnostics/properties.kt +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/properties.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB package test diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/simple.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/simple.kt index 7b58d8b8d41..577c30961ca 100644 --- a/plugins/parcelize/parcelize-compiler/testData/diagnostics/simple.kt +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/simple.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL package test import kotlinx.parcelize.Parcelize diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/unsupportedType.fir.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/unsupportedType.fir.kt new file mode 100644 index 00000000000..e1d1b2cc392 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/unsupportedType.fir.kt @@ -0,0 +1,17 @@ +package test + +import kotlinx.parcelize.Parcelize +import kotlinx.parcelize.RawValue +import android.os.Parcelable + +@Parcelize +class User( + val a: String, + val b: Any, + val c: Any?, + val d: Map, + val e: @RawValue Any?, + val f: @RawValue Map, + val g: Map, + val h: Map<@RawValue Any, List<@RawValue Any>> +) : Parcelable diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/withoutParcelableSupertype.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/withoutParcelableSupertype.kt index 15db9a34601..e645a745617 100644 --- a/plugins/parcelize/parcelize-compiler/testData/diagnostics/withoutParcelableSupertype.kt +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/withoutParcelableSupertype.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL package test import kotlinx.parcelize.Parcelize diff --git a/plugins/parcelize/parcelize-compiler/testData/diagnostics/wrongAnnotationTarget.kt b/plugins/parcelize/parcelize-compiler/testData/diagnostics/wrongAnnotationTarget.kt index c22022d3865..03c50ceb188 100644 --- a/plugins/parcelize/parcelize-compiler/testData/diagnostics/wrongAnnotationTarget.kt +++ b/plugins/parcelize/parcelize-compiler/testData/diagnostics/wrongAnnotationTarget.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL package test import kotlinx.parcelize.Parcelize diff --git a/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/FirParcelizeDiagnosticTestGenerated.java b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/FirParcelizeDiagnosticTestGenerated.java new file mode 100644 index 00000000000..0ea58af1c3f --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/FirParcelizeDiagnosticTestGenerated.java @@ -0,0 +1,115 @@ +/* + * Copyright 2010-2021 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.test.runners; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.TestMetadata; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("plugins/parcelize/parcelize-compiler/testData/diagnostics") +@TestDataPath("$PROJECT_ROOT") +public class FirParcelizeDiagnosticTestGenerated extends AbstractFirParcelizeDiagnosticTest { + @Test + public void testAllFilesPresentInDiagnostics() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/parcelize/parcelize-compiler/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); + } + + @Test + @TestMetadata("constructors.kt") + public void testConstructors() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/constructors.kt"); + } + + @Test + @TestMetadata("customCreator.kt") + public void testCustomCreator() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/customCreator.kt"); + } + + @Test + @TestMetadata("customParcelers.kt") + public void testCustomParcelers() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/customParcelers.kt"); + } + + @Test + @TestMetadata("customWriteToParcel.kt") + public void testCustomWriteToParcel() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/customWriteToParcel.kt"); + } + + @Test + @TestMetadata("delegate.kt") + public void testDelegate() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/delegate.kt"); + } + + @Test + @TestMetadata("deprecatedAnnotations.kt") + public void testDeprecatedAnnotations() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/deprecatedAnnotations.kt"); + } + + @Test + @TestMetadata("emptyPrimaryConstructor.kt") + public void testEmptyPrimaryConstructor() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/emptyPrimaryConstructor.kt"); + } + + @Test + @TestMetadata("kt20062.kt") + public void testKt20062() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/kt20062.kt"); + } + + @Test + @TestMetadata("modality.kt") + public void testModality() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/modality.kt"); + } + + @Test + @TestMetadata("notMagicParcel.kt") + public void testNotMagicParcel() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/notMagicParcel.kt"); + } + + @Test + @TestMetadata("properties.kt") + public void testProperties() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/properties.kt"); + } + + @Test + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/simple.kt"); + } + + @Test + @TestMetadata("unsupportedType.kt") + public void testUnsupportedType() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/unsupportedType.kt"); + } + + @Test + @TestMetadata("withoutParcelableSupertype.kt") + public void testWithoutParcelableSupertype() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/withoutParcelableSupertype.kt"); + } + + @Test + @TestMetadata("wrongAnnotationTarget.kt") + public void testWrongAnnotationTarget() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/diagnostics/wrongAnnotationTarget.kt"); + } +} diff --git a/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeDiagnosticTestGenerated.java b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeDiagnosticTestGenerated.java index f38c2cb461f..75ca91c4beb 100644 --- a/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeDiagnosticTestGenerated.java +++ b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeDiagnosticTestGenerated.java @@ -6,9 +6,8 @@ package org.jetbrains.kotlin.parcelize.test.runners; import com.intellij.testFramework.TestDataPath; -import org.jetbrains.kotlin.test.util.KtTestUtil; import org.jetbrains.kotlin.test.TestMetadata; -import org.junit.jupiter.api.Nested; +import org.jetbrains.kotlin.test.util.KtTestUtil; import org.junit.jupiter.api.Test; import java.io.File; @@ -21,7 +20,7 @@ import java.util.regex.Pattern; public class ParcelizeDiagnosticTestGenerated extends AbstractParcelizeDiagnosticTest { @Test public void testAllFilesPresentInDiagnostics() throws Exception { - KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/parcelize/parcelize-compiler/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), null, true); + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/parcelize/parcelize-compiler/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } @Test diff --git a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractFirParcelizeDiagnosticTest.kt b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractFirParcelizeDiagnosticTest.kt new file mode 100644 index 00000000000..d7a73bd8b26 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractFirParcelizeDiagnosticTest.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2021 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.test.runners + +import org.jetbrains.kotlin.parcelize.test.services.FirFacadeWithParcelizeExtension +import org.jetbrains.kotlin.parcelize.test.services.ParcelizeEnvironmentConfigurator +import org.jetbrains.kotlin.test.bind +import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder +import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives +import org.jetbrains.kotlin.test.frontend.fir.FirFailingTestSuppressor +import org.jetbrains.kotlin.test.frontend.fir.handlers.FirIdenticalChecker +import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerTest +import org.jetbrains.kotlin.test.runners.baseFirDiagnosticTestConfiguration +import org.jetbrains.kotlin.test.services.fir.FirOldFrontendMetaConfigurator + +abstract class AbstractFirParcelizeDiagnosticTest : AbstractKotlinCompilerTest() { + override fun TestConfigurationBuilder.configuration() { + baseFirDiagnosticTestConfiguration(frontendFacade = FirFacadeWithParcelizeExtension) + + defaultDirectives { + +FirDiagnosticsDirectives.ENABLE_PLUGIN_PHASES + } + + useConfigurators(::ParcelizeEnvironmentConfigurator.bind(true)) + + useAfterAnalysisCheckers( + ::FirIdenticalChecker, + ::FirFailingTestSuppressor, + ) + useMetaTestConfigurators(::FirOldFrontendMetaConfigurator) + } +} diff --git a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeBoxTest.kt b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeBoxTest.kt index f9dce526ec9..292b4f7acd2 100644 --- a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeBoxTest.kt +++ b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeBoxTest.kt @@ -5,11 +5,7 @@ package org.jetbrains.kotlin.parcelize.test.runners -import org.jetbrains.kotlin.parcelize.fir.FirParcelizeExtensionRegistrar -import org.jetbrains.kotlin.parcelize.test.services.ParcelizeEnvironmentConfigurator -import org.jetbrains.kotlin.parcelize.test.services.ParcelizeMainClassProvider -import org.jetbrains.kotlin.parcelize.test.services.ParcelizeRuntimeClasspathProvider -import org.jetbrains.kotlin.parcelize.test.services.ParcelizeUtilSourcesProvider +import org.jetbrains.kotlin.parcelize.test.services.* import org.jetbrains.kotlin.test.Constructor import org.jetbrains.kotlin.test.TargetBackend import org.jetbrains.kotlin.test.backend.BlackBoxCodegenSuppressor @@ -20,9 +16,6 @@ import org.jetbrains.kotlin.test.backend.ir.IrBackendInput import org.jetbrains.kotlin.test.backend.ir.JvmIrBackendFacade import org.jetbrains.kotlin.test.bind import org.jetbrains.kotlin.test.builders.* -import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder -import org.jetbrains.kotlin.test.builders.configureClassicFrontendHandlersStep -import org.jetbrains.kotlin.test.builders.configureFirHandlersStep import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.REQUIRES_SEPARATE_PROCESS import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives.DIAGNOSTICS import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives.REPORT_ONLY_EXPLICITLY_DEFINED_DEBUG_INFO @@ -33,7 +26,6 @@ import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendFacade import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact import org.jetbrains.kotlin.test.frontend.classic.handlers.ClassicDiagnosticsHandler import org.jetbrains.kotlin.test.frontend.fir.Fir2IrResultsConverter -import org.jetbrains.kotlin.test.frontend.fir.FirFrontendFacade import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact import org.jetbrains.kotlin.test.frontend.fir.handlers.FirDiagnosticsHandler import org.jetbrains.kotlin.test.model.* @@ -124,11 +116,7 @@ open class AbstractParcelizeFirBoxTest : AbstractParcelizeBoxTestBase> - get() = { testServices -> - FirFrontendFacade(testServices) { - it.registerExtensions(FirParcelizeExtensionRegistrar().configure()) - } - } + get() = FirFacadeWithParcelizeExtension override val frontendToBackendConverter: Constructor> get() = ::Fir2IrResultsConverter diff --git a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeDiagnosticTest.kt b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeDiagnosticTest.kt index 3d32c9c028d..c55315b6e36 100644 --- a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeDiagnosticTest.kt +++ b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeDiagnosticTest.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives.DIAGNOSTICS import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives.REPORT_JVM_DIAGNOSTICS_ON_FRONTEND import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.USE_PSI_CLASS_FILES_READING import org.jetbrains.kotlin.test.frontend.classic.handlers.ClassicDiagnosticsHandler +import org.jetbrains.kotlin.test.frontend.classic.handlers.FirTestDataConsistencyHandler import org.jetbrains.kotlin.test.model.DependencyKind import org.jetbrains.kotlin.test.model.FrontendKinds import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerTest @@ -48,5 +49,7 @@ abstract class AbstractParcelizeDiagnosticTest : AbstractKotlinCompilerTest() { classicFrontendHandlersStep { useHandlers(::ClassicDiagnosticsHandler) } + + useAfterAnalysisCheckers(::FirTestDataConsistencyHandler) } } diff --git a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/services/FirFacadeWithParcelizeExtension.kt b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/services/FirFacadeWithParcelizeExtension.kt new file mode 100644 index 00000000000..541bc9e0d9a --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/services/FirFacadeWithParcelizeExtension.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2010-2021 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.test.services + +import org.jetbrains.kotlin.parcelize.fir.FirParcelizeExtensionRegistrar +import org.jetbrains.kotlin.test.Constructor +import org.jetbrains.kotlin.test.frontend.fir.FirFrontendFacade + +val FirFacadeWithParcelizeExtension: Constructor = { testServices -> + FirFrontendFacade(testServices) { + it.registerExtensions(FirParcelizeExtensionRegistrar().configure()) + } +}